☰
InvokeAI Call Saved Workflow 架构深度解析:从调用边界到嵌套执行的工作流复用引擎
2026/9/30 2:44:57 网站建设 项目流程

InvokeAI Call Saved Workflow 架构深度解析:从调用边界到嵌套执行的工作流复用引擎

【免费下载链接】InvokeAIInvoke is a leading creative engine for Stable Diffusion models, empowering professionals, artists, and enthusiasts to generate and create visual media using the latest AI-driven technologies. The solution offers an industry leading WebUI, and serves as the foundation for multiple commercial products.项目地址: https://gitcode.com/GitHub_Trending/in/InvokeAI

导读

本文基于 InvokeAI 仓库中的 调用已保存工作流架构文档,完整讲解 InvokeAI 如何将call_saved_workflow打造成引擎原生的"工作流调用边界":父工作流按 ID 调用一个已保存工作流、编辑器根据子工作流暴露的表单字段动态重绘调用节点、运行时在调用节点处挂起父工作流、以独立依赖执行的方式运行子工作流、捕获显式命名返回值后再恢复父工作流。读完本文,你将掌握这一特性的节点模型、执行契约、队列生命周期、批量展开规则、嵌套与递归约束,以及对应的源码实现位置,可直接在真实项目中复现与验证这套架构。

一、背景与设计目标:为什么需要"工作流调用"

在 InvokeAI 中,工作流是用户在编辑器中绘制的节点图,可保存到工作流库供后续复用。此前,复用一个已保存工作流的方式局限于"前端动态节点"或"编译期图内联",二者都有明显缺陷:

  • 前端动态节点:可调用逻辑依附于前端,外部提交的图无法复用;
  • 编译期图内联(graph inliner):把子工作流整个展开进父图,会导致执行图膨胀、破坏工作流边界,且难以表达显式返回值。

因此,本特性的长期目标是让CallSavedWorkflowInvocation成为引擎原生的工作流调用边界(call boundary),具体能力包括:

  • 父工作流按workflow_id调用一个已保存工作流;
  • 调用节点根据所选工作流暴露的表单字段在编辑器中动态重绘;
  • 父节点的值与入边连接作为调用参数绑定到这些暴露字段;
  • 执行在调用节点处挂起,将所选工作流作为依赖的子工作流执行运行,捕获显式返回值后恢复父工作流;
  • 架构同时服务于前端图与使用同一节点类型的外部提交图。

该特性的核心节点与运行时脚手架在源码中均已就位:真正的调用节点位于 call_saved_workflow.py,真正的返回节点位于 workflow_return.py,运行时协调逻辑位于 workflow_call_runtime.py。

实现优先级:正确架构优先于最快路径

设计文档明确要求每个增量都必须满足三点:可在隔离环境中测试、与长期架构兼容、不破坏既有代码与现有工作流执行行为。速度不是本阶段的首要目标,首要目标是走向持久化设计,避免引入日后需要回退的一次性执行语义。

二、当前实现状态总览

已实现的节点与编辑器能力

  • 真实调用节点call_saved_workflow与真实返回节点workflow_return均已存在;
  • 通过workflow_return_value(构造单个命名返回值)、workflow_return(汇总返回)、调用侧的workflow_return_get(按键提取)实现命名返回;
  • workflow_return可接受一个键值返回成员,或一个收集好的返回成员列表,然后输出命名的values: dict[str, Any]映射;
  • 每个工作流只允许一个workflow_return节点,前端校验与 Python 校验双重强制;
  • 前端提供基于可复用SavedWorkflowFieldUI 类型的已保存工作流选择器;
  • 节点根据所选已保存工作流的暴露表单字段动态重绘;
  • 动态字段值随父工作流持久化;
  • 切换工作流时,具有匹配暴露字段标识与兼容类型的入边被保留;
  • 不兼容或不再暴露的入边在编辑器中移除;
  • 后端已实现workflow_id的存在性与访问权限校验。

已实现的运行时脚手架

GraphExecutionState持久化了工作流调用运行时状态,新增字段包括workflow_call_stack、workflow_call_history、workflow_call_parent、waiting_workflow_call、waiting_workflow_call_execution、waiting_workflow_call_child_session、max_workflow_call_depth。这些字段定义于 graph.py,其中max_workflow_call_depth默认值为4,作为嵌套与递归调用的运行时深度上限。

核心运行时行为:

  • 嵌套与递归调用由调用栈表示,运行时深度上限为 4;
  • 父子调用身份在运行时状态中显式化:父会话等待期间跟踪活动WorkflowCallExecution记录,已完成与失败的调用保留在workflow_call_history,子会话携带workflow_call_parent引用回父调用关系;
  • 父会话等待子调用期间,GraphExecutionState.next()不返回任何可运行节点,is_complete()保持false;
  • DefaultSessionRunner.run_node()将call_saved_workflow视为调用边界而非普通可执行节点,见 session_processor_default.py;
  • 进入边界时,runner 校验所选工作流、构建调用帧、将已保存工作流 JSON 转换为后端Graph、校验并应用父调用参数、创建子GraphExecutionState、将子会话挂到等待中的父会话上。

队列生命周期与批量能力

  • WorkflowCallCoordinator负责调用专用设置(构建子图、应用父参数、创建子状态、挂起父会话并入队子队列项);
  • WorkflowCallQueueLifecycle负责队列可见的父子生命周期(运行子队列项、子成功后恢复等待中的父、以子workflow_return值完成父调用节点、子失败时级联失败);
  • 子SessionQueueItem携带workflow_call_id、parent_item_id、parent_session_id、root_item_id、workflow_call_depth等关系元数据,session_queue表也有对应持久化列;
  • 父队列项在挂起期间进入真正的waiting状态;
  • 动态调用参数端到端执行:字面量动态值在构图期序列化进隐藏的workflow_inputs负载;过期的隐藏值被忽略;类型兼容时保留既有动态输入值,类型变化则重置为子工作流当前初始值;连接的动态值作为调用边界专用边在运行时从父结果解析;
  • 队列生命周期语义完整:父挂起waiting、子成功恢复父、子失败向上级联、取消父级联取消子孙、取消子级联取消等待中的父、批量失败后取消剩余兄弟项、删除链上任一项即删除整条链、cancel_all_except_current/delete_all_except_current保留活动项及其祖先/后代链、重试以根为导向(UI 上子队列行只保留Cancel、隐藏Retry);
  • 子队列行创建为"失败即清理":若边界设置失败时已插入部分子行,先删除这些子行再失败父调用;
  • 子队列扇出受剩余队列容量约束(而非仅全局队列大小):超出剩余 pending 容量的调用直接失败,子插入与 pending 容量重检在同一数据库事务内完成。

转换辅助与兼容性门禁

  • workflow_graph_builder.py 将已保存工作流 JSON 转换为可执行后端Graph,目前支持本特性所需的 invocation 节点子集,扁平化 connector 节点,并在存在连接时省略显式目标字段值,与前端构图语义一致;
  • 它同时充当首个显式"可调用工作流兼容性门禁":所选工作流必须恰好含一个workflow_return节点;普通非生成器上游节点产生的连接批量输入会以清晰的 unsupported-feature 错误提前失败;畸形批量输入布线(含一个批量字段多个连接输入)报告为unsupported_batch_input兼容性错误;混合受支持批量节点与无关生成器节点的子工作流被拒绝;不支持的被调用工作流在任何子队列行创建前即被拒绝;
  • 兼容性元数据通过工作流库 API 暴露:工作流列表项与详情响应包含call_saved_workflow_compatibility;列表项使用结构化的生成器批量检查以避免枚举 board 中每张图片;选择器用该元数据禁用不支持的工作流;被选中的不支持工作流仍可渲染并显示明确的不支持状态与本地化原因。

尚未实现的部分

  • 由普通非生成器上游节点产生批量值的连接批量子输入仍不支持,须以清晰领域错误失败;
  • 混合受支持批量节点与无关生成器节点的子工作流仍不支持;
  • 更广泛的子工作流兼容性覆盖仍需从真实的不支持形态扩展,而非试图通过当前 graph-builder 路径解释每个仅前端的工作流表示;
  • 当前工作流调用队列生命周期仍通过专用运行时类实现,尚未泛化为完整父子调度器模型。

文档的结论是:编辑器契约与父侧运行时调用边界已基本就位,子执行、参数转发、显式子返回捕获、父挂起状态、队列可见子行与向上失败级联均可工作,剩余主要运行时工作是加固并泛化父子调度器模型。

三、核心节点模型:调用节点与返回节点

3.1 CallSavedWorkflowInvocation

调用节点定义在 call_saved_workflow.py,注册类型为call_saved_workflow,标注为Classification.Beta,use_cache=False(调用结果不可缓存)。其核心字段:

  • workflow_id: str:所选已保存工作流 ID,由编辑器 UI 管理,ui_type=UIType.SavedWorkflow;
  • workflow_inputs: dict[str, Any]:所选工作流暴露输入的字面量值,由编辑器 UI 管理,ui_hidden=True隐藏。

动态输入字段名以saved_workflow_input::为前缀(常量CALL_SAVED_WORKFLOW_DYNAMIC_FIELD_PREFIX),形如saved_workflow_input::{nodeId}::{fieldName}。is_call_saved_workflow_dynamic_input()与parse_call_saved_workflow_dynamic_input()分别用于判断与解析这类字段,解析出子工作流中的node_id与input_field_name。

validate_selected_workflow()执行双重校验:先通过workflow_records.get(workflow_id)确认工作流存在(WorkflowNotFoundError转为清晰的 ValueError);在多用户模式下再校验访问权——用户必须是工作流所有者、工作流是 Default 类别、工作流公开,或用户是管理员,否则抛出"所选已保存工作流对该用户不可访问"错误。该访问控制逻辑与设计文档"运行时必须强制与别处相同的已保存工作流访问规则"完全一致。

注意invoke()本身只做校验并返回空values={}的WorkflowReturnOutput——真正的执行由 runner 的边界逻辑接管,这也印证了"调用节点不是普通可执行节点"的设计。

3.2 命名返回节点族

返回节点全部定义在 workflow_return.py:

节点/输出类型职责
WorkflowReturnOutput输出values: dict[str, Any],即显式命名返回值映射
WorkflowReturnValueField一个命名返回项,含key: str与value: Any
workflow_return_value由 key 与连接的值构造单个WorkflowReturnValueField;key 为空时报错
workflow_return接受单个或列表形式的返回成员,合并为命名映射;同一执行内重复 key 直接报错
workflow_return_get调用侧提取节点,从命名映射中按键取值;缺失 key 明确失败

workflow_return的合并逻辑值得注意:输入既可以是单个WorkflowReturnValueField,也可以是列表(对应编辑器里"一个返回节点直接接收一个返回值"或"收集多个返回值后连接列表"两种布线方式),最终输出WorkflowReturnOutput(values=named_values),其中空 key 与重复 key 都会抛出明确错误。

3.3 返回值流

设计文档定义了严格的返回值流转路径,且明确"返回值不应写回已保存工作流记录,也不应从前端状态推导":

  1. 子工作流像普通节点输出一样计算命名返回成员;
  2. 子工作流将单个返回成员直接连到workflow_return.values,或收集多个返回成员后把列表连到workflow_return.values;
  3. 子工作流到达workflow_return时,运行时把解析出的命名映射捕获为子工作流结果;
  4. 子工作流结果存入子执行状态;
  5. 该结果交还给挂起的父调用帧;
  6. 父call_saved_workflow节点以该命名映射完成;
  7. 父图恢复执行。

四、执行契约详解

4.1 可调用接口

已保存工作流的可调用接口由其已保存工作流 JSON 定义:主来源是workflow.form,旧工作流回退到workflow.exposedFields。只有子工作流表单暴露的字段才是可调用输入,图中存在但未暴露的内部输入不属于公共调用接口。在 workflow_graph_builder.py 中,_collect_exposed_inputs_from_form()从表单根元素出发广度优先遍历node-field元素,把fieldIdentifier中的nodeId与fieldName组合成动态输入名;get_exposed_workflow_input_names()则是兼容性分析中枚举暴露输入的入口。

4.2 输入参数与运行时绑定

call_saved_workflow根据所选工作流的可调用接口在编辑器中暴露动态输入。保存工作流选择器会把类型化搜索文本发送到工作流列表端点,保证大工作流库即使尚未加载进 combobox 分页也能被发现。

每个动态输入必须具备:

  • 稳定的外部句柄名;
  • 类型;
  • 子工作流定义时的默认值(若有);
  • 可用的用户可见标签与描述(若有)。

当前快速路径身份基于子nodeId + fieldName,在编辑器中短期可接受;但如果子工作流频繁复制或重构,长期应引入稳定的接口 ID。

运行时父节点到达call_saved_workflow时:引擎解析workflow_id→ 加载所选子工作流记录 → 从保存 JSON 重建可调用接口 → 从父节点动态输入收集参数值 → 启动依赖的子工作流执行。参数值来源有二:父字面量字段值、解析后的入边连接到调用节点动态输入。对批量感知的子工作流,父调用边界仍传递普通暴露表单输入,批量应从子工作流自身的内部批量节点或生成器涌现,而非单独的调用侧批量协议。

4.3 子工作流执行语义

子工作流作为独立的依赖执行上下文运行,而非父图的内联副本。期望语义:父在调用节点暂停 → 子执行在适当处继承上下文 → 子完成或失败 → 仅在子成功时父恢复。这要求队列/会话/运行时层存在显式的父子执行关系。

当前限制:graph-builder 路径仍只重建子工作流的普通 invocation 子集;直接批量专用子工作流与生成器驱动批量子工作流已绕过该路径使用队列批量展开;连接批量输入(非生成器上游产生)仍不支持;当前队列可见子执行路径仍依赖WorkflowCallCoordinator直接恢复/失败父,而非更通用的队列调度抽象。

4.4 队列生命周期契约

这是当前用户可见契约的核心部分:

  • 根/父队列项在子调用挂起期间进入waiting;
  • 子工作流执行是携带显式父子关系元数据的真实队列行;
  • 子完成恢复挂起的父并交还正常队列执行;
  • 子失败使挂起的父调用节点失败,并通过祖先链向上级联;
  • 取消操作是链感知的:取消等待中的父会取消后代;取消子会取消等待中的祖先;批量兄弟项中一个失败后取消其余会包含这些兄弟项的嵌套后代;"除当前外全部取消/删除"保留活动项及其父/子链(含处理器交接窗口期waiting父 +pending子),同时仍取消/删除无关等待链;
  • 重试操作是根感知的:重试根项创建新的根执行;后端把对子项的重试规范化为对根的重试;重试与整链删除的授权按根队列项所有者校验;子队列行不暴露直接重试入口;重试 websocket 投递按所有者隔离——管理员重试多个用户拥有的根时,每个非管理员用户只收到自己根的重试 item id;
  • 工作流实时更新 socket 在认证多用户与未认证单用户模式下都加入工作流事件房间;单用户模式下 CRUD 事件只发给 admin 房间以避免重复投递;
  • 公开转私有过渡发出 schema 定义的workflow_access_revoked事件给共享订阅者,非所有者/非管理员客户端清除引用,所有者与管理员保留访问;
  • 选择器分别查询自有/默认工作流与公开共享工作流,按工作流 id 合并,并在 combobox 菜单到达末尾时翻页;
  • 队列状态事件必须保持用户隔离:QueueItemStatusChangedEvent.queue_status可保留全局聚合计数,但内嵌的当前项标识(item_id、session_id、batch_id)只有在当前进行项属于事件所有者或构建于 admin/全局上下文时才出现;工作流调用子入队事件使用与普通状态转换相同的所有者感知脱敏。

4.5 批量子工作流(Batch Child Workflows)

当前实现支持四类直接批量专用子工作流:image_batch、string_batch、integer_batch、float_batch;并支持生成器驱动的批量子工作流:integer_generator、float_generator、string_generator、image_generator(使用image_generator_images_from_board)。

当前语义:

  • 批量专用节点在普通图校验前从可执行子图中移除;
  • 为这些节点供数的受支持生成器节点同样被移除;
  • 它们的出边被转换为队列批量替换(batch substitutions);
  • 未分组批量节点按笛卡尔积展开;
  • 分组批量节点按batch_group_id按位拉链(zip);
  • 每次展开创建一条子队列行(一个批量会话);
  • 受支持生成器的值形状在队列批量展开前解析为具体批量项;
  • 声明的生成器计数在解析前若超出剩余子容量即被拒绝;
  • 笛卡尔积大小在生成会话前用算术计算,而非物化乘积;
  • 批量输出可直接喂给命名workflow_return_value.value,每个展开的子返回该 key 的一个值;
  • 父恢复会等待该调用关联的所有子行;
  • 父返回聚合产生values: dict[str, list[Any]],每个 key 对应每个子行的一个值;
  • 一次批量调用中所有子行必须返回相同 key 集合,key 不匹配会明确失败父调用;
  • 任一子行失败时取消其余兄弟子行并失败父调用;
  • 生成器驱动图片批量必须尊重 board 访问:调用者可展开自有 board,管理员可展开任意 board,共享/公开 board 可被其他用户展开,不可访问的私有 board 必须在图片展开前失败,避免跨用户泄露 board 内容。

当前生成器覆盖范围:

  • 整数生成器:算术序列、线性分布、解析字符串、带种子的均匀随机分布;
  • 浮点生成器:算术序列、线性分布、解析字符串、带种子的均匀随机分布;
  • 字符串生成器:解析字符串、动态提示组合(dynamic prompts combinatorial)、动态提示随机;
  • 图片生成器:从 board 取图。

仍然不支持:批量值由非生成器上游节点产生的连接批量输入。

文档给出的批量调用通俗流程:父工作流到达call_saved_workflow→ 父暂停进入waiting→ 执行前检查子工作流 → 若含受支持批量输入,一次调用展开为多次子执行 → 每次展开成为自己的队列行 → 每个子队列行保留替换后的批量field_values(与普通批量队列行一致)→ 子队列行独立运行 → 父在所有子队列行完成后才恢复 → 每个子执行产生自己的命名workflow_return.values→ 父聚合为values: dict[str, list[Any]]→ 调用节点以该命名映射完成,父继续。

展开规则示例(文档原例):

  • 未分组输入[1, 2]与[10, 20]产生 4 个子执行:(1,10)、(1,20)、(2,10)、(2,20);
  • 相同batch_group_id的分组输入[1,2,3]与[10,20,30]产生 3 个子执行:(1,10)、(2,20)、(3,30)。

4.6 易错点(Tricky Areas)

契约中以下语义最容易误读,代码与测试中必须保持显式:

  • 等待与恢复:waiting状态的父队列行是挂起而非完成;父只有在关联该调用的每个子队列行都达到终态后才恢复;
  • 返回聚合:每个子队列行返回自己的命名workflow_return.values;批量调用时父节点输出为values: dict[str, list[Any]];一次批量调用内所有子行必须返回相同 key 集合以保证每个列表按行对齐;非批量子若需在单个 key 下返回多张图片,须先把图片收集进一个列表值再返回该 key;
  • 兄弟失败行为:批量调用中一个子队列行失败 → 取消同一调用的其余兄弟子行;父返回聚合拒绝某已完成子行 → 同样取消其余兄弟子行;兄弟取消后父调用失败;若该父本身是另一调用的子,失败继续沿祖先链向上;
  • 取消行为:取消等待中的父会取消后代子行;取消子行会取消等待中的祖先;取消就是取消,不应改写成普通失败语义;启动恢复会取消任何中断的in_progress或waiting调用链(含 pending 后代),防止重启后挂起父永远等不到不会回报的子行;
  • 重试行为:重试以根为导向;UI 不应直接重试子队列行;后端对子 id 的重试应规范化为根调用链,而非创建孤立的仅子重跑。

4.7 错误传播

子执行失败时:调用节点失败;除非后续设计加入显式错误处理语义,父工作流失败。首个实现中失败传播保持简单且严格。

4.8 访问控制

运行时必须强制与已保存工作流其他使用处相同的访问规则——调用者只有在其被允许在运行时访问该已保存工作流时才能执行子工作流。这一点即使父工作流是在子工作流曾经可见的上下文中编写的也依然成立(即运行时复查而非信任创作期可见性)。源码中CallSavedWorkflowInvocation.validate_selected_workflow()正是这一规则的实现。

4.9 递归与嵌套

嵌套与递归的call_saved_workflow执行被允许但有界:

  • 允许嵌套调用;
  • 允许递归调用;
  • 最大调用深度上限为 4 个调用帧;
  • 深度上限在运行时基于活动调用栈强制,而非仅静态校验。

这在 graph.py 的build_workflow_call_frame()中体现:next_depth = get_workflow_call_depth() + 1,超过max_workflow_call_depth(默认 4)即抛"Maximum workflow call depth exceeded"错误。这样既允许合法的递归或有条件终止的工作流结构,又防止无界调用增长。

五、运行时组件与源码落点

5.1 调用边界入口:DefaultSessionRunner

在 session_processor_default.py 的run_node()路径中,检测到CallSavedWorkflowInvocation时不再走普通invoke_internal执行,而是先validate_selected_workflow(context),再调用workflow_call_coordinator.begin_workflow_call_boundary(invocation, queue_item, workflow_record)后返回——这是"调用边界"在引擎执行栈中的权威落点,保证外部提交图与前端图走同一路径。

5.2 WorkflowCallCoordinator

定义于 workflow_call_runtime.py,负责调用专用设置:

  • 容量预检:max_queue_size - pending计算剩余容量,不足即失败;
  • build_workflow_call_frame()构建调用帧(含深度检查);
  • _collect_call_saved_workflow_inputs()收集字面量workflow_inputs与已解析的动态入边连接值;
  • build_child_workflow_session_results()构建子会话(见 5.5);
  • begin_waiting_on_workflow_call()挂起父会话并附加子会话;
  • 在同一事务内保存父会话、逐条enqueue_workflow_call_child()入队子行、记录子行 id、suspend_queue_item()挂起父;
  • 失败即清理:若部分子行已插入而后失败,先delete_queue_items_by_id()删除这些子行再让父失败。

build_child_queue_item()展示了子队列行的关系元数据构造:workflow_call_id、parent_item_id、parent_session_id、root_item_id(无祖先时回退为自身 item_id)、workflow_call_depth、field_values。

5.3 WorkflowCallQueueLifecycle

同文件中的WorkflowCallQueueLifecycle负责队列可见的父子生命周期,核心方法:

  • get_waiting_workflow_call_invocation():从waiting_workflow_call帧定位父节点上的调用节点;
  • get_child_workflow_return_output():从子会话的source_prepared_mapping定位唯一的workflow_return执行并取出WorkflowReturnOutput;
  • _resume_parent_from_completed_child():子完成后读取返回输出,调用父会话的record_waiting_workflow_call_child_completion()记录并判断是否所有子行均完成;聚合失败时取消其余子并失败父;全部完成后以聚合值complete()父调用节点、complete_queue_item()或resume_queue_item()恢复父;若父自身也是子,则递归向上恢复;
  • _fail_parent_from_failed_child():子失败时取消该调用其余子、以子错误消息失败父、并沿parent_item_id链向上级联;
  • _cancel_parent_from_canceled_child():子取消时取消等待中的父;
  • run_queue_item():作为子队列行的统一执行入口——先run()子会话,再根据子终态(completed/failed/canceled)分派到上述恢复/失败/取消逻辑;父链上某项已被删除(如清空队列)则安全返回。

5.4 GraphExecutionState 的调用栈

graph.py 中的WorkflowCallFrame表示嵌套调用链中的一个帧(含prepared_call_node_id、source_call_node_id、workflow_id、depth等);GraphExecutionState用workflow_call_stack、workflow_call_history、waiting_workflow_call、waiting_workflow_call_execution、waiting_workflow_call_child_session等字段描述等待状态,并通过is_waiting_on_workflow_call()(waiting_workflow_call is not None)、get_workflow_call_depth()、create_child_workflow_execution_state()支撑嵌套执行——后者把[当前调用栈 + 新帧]与继承的max_workflow_call_depth传给子状态。父会话等待期间next()返回无可运行节点、is_complete()为 false 的实现也在此。

5.5 WorkflowGraphBuilder 与批量会话构建

workflow_graph_builder.py 是"已保存工作流 JSON → 后端 Graph"的转换器:只处理 invocation 节点子集,扁平化 connector 节点(常量CONNECTOR_INPUT_HANDLE = "in"、CONNECTOR_OUTPUT_HANDLE = "out"),存在连接时省略显式目标字段值;异常类型UnsupportedWorkflowNodeError与InvalidWorkflowInputError分别对应不支持节点与非法输入。与之配套的 workflow_call_batch.py 提供build_child_workflow_sessions()/build_child_workflow_session_results(),实现批量展开、生成器值解析、笛卡尔积算术计算与每个展开子会话的构建。

5.6 兼容性元数据

workflow_call_compatibility.py 实现get_workflow_call_compatibility():先统计workflow_return节点数(0 个 →MissingWorkflowReturn,多个 →MultipleWorkflowReturn),再通过_build_compatibility_workflow_inputs()为必需暴露输入合成占位值(str→""、int→0、float→0.0、bool→False、list→[]、dict→{}、ImageField→占位图片名等),保证"调用者一旦提供暴露值即有效"的工作流不会被过早禁用;随后尝试构建子会话,将InvalidWorkflowInputError、UnsupportedWorkflowNodeError(含批量相关消息归类为UnsupportedBatchInput)、TooManySessionsError(→ExceedsCapacity)映射为结构化WorkflowCallCompatibilityReason。该结果随工作流库 API 响应(列表项与详情)暴露给前端选择器。

六、命名返回实现阶段

设计文档把命名返回拆为五个阶段,并给出每阶段的契约与"先写测试"清单:

  • Stage 1 后端返回契约(已实现,有后端 invocation 测试):WorkflowReturnValueField存一个key: str+value: Any;workflow_return_value构造单个字段;workflow_return接受单成员或成员列表;WorkflowReturnOutput暴露values: dict[str, Any];非批量单次执行中重复 key 无效。测试覆盖:单键值对发射、单/多成员命名映射、重复 key 拒绝。
  • Stage 2 调用侧提取原语(已实现):workflow_return_get接受命名映射与 key,输出Any;缺失 key 清晰失败。测试覆盖:提取既有 key、缺失 key 报错、Any值可经既有连接兼容规则喂给下游类型化节点。
  • Stage 3 运行时传播(已实现):非批量子执行返回values: dict[str, Any],call_saved_workflow在输出暴露该映射;失败/取消/重试生命周期行为不变。测试覆盖:{image: ...}完成父输出、提取节点在父恢复后消费、缺失/非法workflow_return节点以既有清晰错误失败。
  • Stage 4 批量返回聚合(已实现):每个子行一个命名映射,父聚合为dict[str, list[Any]];同调用内 key 集合一致;跨子行重复 key 是正常聚合路径,单子行内重复 key 仍非法。测试覆盖:批量子产出{image: [image_1, image_2, ...]}、兄弟失败取消与父失败、单行重复 key 拒绝而非静默聚合。
  • Stage 5 前端 schema/UI/文档(基本实现):生成 schema/类型含返回字段、返回值节点与提取节点;可见 UI 字符串经en.json本地化;call_saved_workflow暴露命名返回映射输出;用户可把它接到workflow_return_get。前端连接/类型测试覆盖返回收集布线、workflow_return_value.value → workflow_return.values直连、call_saved_workflow.values → workflow_return_get.values。

七、前端职责与后续演进

长期设计中前端只负责编辑器期行为:选择已保存工作流;按子可调用接口重绘动态输入;持久化动态字段及其值;切换选择时保留兼容入边;以可预测方式清理不兼容边与失效选择;利用后端兼容性元数据避免把不支持的工作流呈现为可调用选项(兼容性分析已通过合成占位值容忍必需暴露输入)。

潜在未来优化:增加返回规范化可调用工作流接口的后端端点,让前端免于重新解析完整保存负载来重绘节点,并为漂移检测提供后端权威的接口哈希。

八、测试覆盖与下一步

已覆盖的测试(对应文档"Tests Needed Going Forward"清单):GraphExecutionState上的调用栈与等待状态;深度上限强制;等待阻塞调度;等待中父会话不完成;runner 边界入口;校验失败与深度失败仍走普通节点错误路径;子工作流 JSON 转后端Graph;子图构建失败不使父残留部分等待状态;子状态挂到等待中的父会话;协调器负责的子执行完成父队列项而非卡在in_progress;字面量与连接动态参数运行时应用到子图;未暴露动态参数运行时被拒;子workflow_return输出捕获并成为父调用输出;命名返回值的构造、传播、按键提取与dict[str, list[Any]]批量聚合;无workflow_return节点的子工作流被调用时干净失败;子SessionQueueItem事件携带稳定调用关系元数据;父子恢复与失败经队列可见子行传播;有界栈深度的嵌套运行;直接与生成器驱动批量专用子工作流经子行展开;暴露输入、缺失/多返回、命名批量返回形态与不支持批量布线的兼容性元数据。

后续增量仍需:任何新支持的批量/生成器形态在契约变化时的聚焦测试;仅在另一特性需要可复用依赖队列项时,才考虑把专用工作流调用队列生命周期迁移到更通用的调度器/队列生命周期模型。

设计文档给出的推荐立即下一步是:停止添加特性切片(除非关闭具体正确性缺口或解锁真实用户工作流);通过评审、定向测试运行与清理过时设计文档措辞来稳定当前分支;把从WorkflowCallQueueLifecycle到泛化父子队列生命周期的迁移视为更大的架构切片,而非琐碎跟进工作。

结语

当前分支已到关键节点:父调用边界状态存在;可从所选已保存工作流创建子执行状态;子执行、参数转发、显式返回传播、父挂起状态、队列可见子行与向上失败级联都通过现有 coordinator + queue 路径工作;但长期泛化的父子调度语义仍未实现。若你需要在 InvokeAI 上继续开发该特性,建议从阅读 call_saved_workflow.py、workflow_return.py、workflow_call_runtime.py 与 workflow_graph_builder.py 四份源码入手,配合tests/app/invocations/与tests/app/services/下的相关测试用例验证执行契约,再按"队列生命周期契约"一节逐步验证取消、重试、批量与级联失败行为。

【免费下载链接】InvokeAIInvoke is a leading creative engine for Stable Diffusion models, empowering professionals, artists, and enthusiasts to generate and create visual media using the latest AI-driven technologies. The solution offers an industry leading WebUI, and serves as the foundation for multiple commercial products.项目地址: https://gitcode.com/GitHub_Trending/in/InvokeAI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询