Prhub

#33849 [diffusion] gate fast VAE paths by quality

原始 PR 作者 mickqian 合并时间 2026-08-07 12:39 文件变更 10 提交数 1 评论 3 代码增减 +105 / -37

执行摘要

quality 门控 VAE 快速路径,打通 OpenAI 图像与视频 API

PR body 明确说明:“Fast VAE paths must be gated by the explicit output-quality mode so incompatible requests retain the correct decode path.” 快速 VAE 路径必须由显式输出质量档位门控,不兼容的请求应保留正确解码路径。此前 #33818 已在内部实现按 quality=“high” 启用的 FLUX.2 / AutoencoderKL 快速路径,但 quality 未暴露到 OpenAI 图像与视频 API,用户无法显式选择;同时门控类 VaeFastPathGate 定义在 flux2_vae_cuda_opt.py 中,由 wan_vae_cuda_opt.py 反向 import,解码阶段用手写 getattr + try/finally 管理开关。本 PR 解决两个问题:统一门控实现与生命周期管理;把 quality 变成公共 API 参数。

值得精读。核心看点:①用 WeakKeyDictionary 做 nn.Module 级注册表,兼顾对象身份匹配与 GC 友好;②contextmanager 的 previous_enabled 恢复模式让门控可嵌套、异常安全,是请求级开关的干净范式;③_runtime_sampling_quality 把 OpenAI 的 auto 默认值隔离在采样契约之外,是 API 兼容性处理的范例。对要在 diffusion 运行时新增请求级行为开关或维护 OpenAI 兼容入口的开发者有直接参考价值。建议结合 #33818 一起阅读以理解快速路径本体。

讨论亮点

本 PR 没有任何 review 评论,3 条 issue 评论均为作者 mickqian 触发的 CI 重跑指令(/tag-and-rerun-ci 两次、/tag-and-rerun-ci extra 一次),随后作者自审自合。门控设计(按 quality 显式门控、安装 fail-closed、lossless 走逐比特一致路径)的主要权衡在前置 PR #33818 中已经讨论并定型,本 PR 属于收尾与 API 打通,无遗留争议。

实现拆解

  1. 新增共享门控模块 python/sglang/multimodal_gen/runtime/models/vaes/fast_path_gate.py:定义 VaeFastPathGate(每 VAE 一份的可变开关)、register_vae_fast_path_gate(以 WeakKeyDictionary 按 VAE 模块对象登记,弱引用保证 VAE 释放后条目自动消失)、use_vae_fast_path(contextmanager,先保存 previous_enabled 再覆盖并在 finally 恢复,异常安全且支持嵌套)。
  2. VAE 优化模块去重flux2_vae_cuda_opt.pywan_vae_cuda_opt.py 删除本地门控类与 GATE_ATTR,改为从共享模块导入并调用 register_vae_fast_path_gate(vae, gate) 登记;wan_vae_cuda_opt.py 因此不再反向依赖 flux2_vae_cuda_opt.py_decoder_layout_forward 的布局切换日志由 info 降为 debug,避免每请求刷屏。
  3. 解码流水线接入decoding.pyDecodingStage.forward 将手写 getattr + try/finally 替换为 with use_vae_fast_path(vae, batch.sampling_params.quality == "high"),主解码与 trajectory 批量解码同处一个上下文内;注意这里由 getattr(..., None) 兜底改成了直接属性访问。
  4. OpenAI API 透传image_api.py 的 generations 与 edits 新增 _runtime_sampling_quality 过滤(None 与 "auto" 映射为 None,避免 OpenAI 默认值进入 SGLang 采样契约),edits 端点新增 quality: Optional[str] = Form(None) 表单参数;video_api.py 把 quality 加入额外字段白名单并在 _build_video_sampling_params 中转发。quality 与 output_quality 语义分离:后者只控制输出文件压缩。
  5. 测试、文档与 CI:新增 test_vae_fast_path_gate.py(验证 decode 作用域、嵌套复位与未登记透传),test_openai_image_api.py 覆盖 _runtime_sampling_quality 的默认值保持,test_video_api_profiling.py 验证 quality 转发;openai_api.mdx 补充 quality 参数说明。CI 通过 /tag-and-rerun-ci 触发,未在本地运行 SGLang-Diffusion 测试。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/models/vaes/fast_path_gate.py 门控模块 added 8.68
python/sglang/multimodal_gen/runtime/models/vaes/flux2_vae_cuda_opt.py VAE 解码 modified 7.34
python/sglang/multimodal_gen/runtime/entrypoints/openai/image_api.py 图像入口 modified 6.44
python/sglang/multimodal_gen/runtime/pipelines_core/stages/decoding.py 解码流水线 modified 6.42
python/sglang/multimodal_gen/runtime/models/vaes/wan_vae_cuda_opt.py VAE 解码 modified 5.68
python/sglang/multimodal_gen/runtime/entrypoints/openai/video_api.py 视频入口 modified 4.7
python/sglang/multimodal_gen/test/unit/test_vae_fast_path_gate.py 单元测试 added 5.55
python/sglang/multimodal_gen/test/unit/test_openai_image_api.py 单元测试 modified 4.64
python/sglang/multimodal_gen/test/unit/test_video_api_profiling.py 单元测试 modified 3.63
docs/docs/sglang-diffusion/api/openai_api.mdx 公开文档 modified 2.93

关键符号

use_vae_fast_path register_vae_fast_path_gate VaeFastPathGate _runtime_sampling_quality DecodingStage.forward _install_decoder_fast_paths maybe_optimize_flux2_vae maybe_optimize_autoencoder_kl maybe_optimize_wan_vae _build_video_sampling_params

关键源码片段

python/sglang/multimodal_gen/runtime/entrypoints/openai/image_api.py entrypoint

generations 与 edits 入口透传 quality,新增 _runtime_sampling_quality 过滤 OpenAI 的 auto 默认值。

# python/sglang/multimodal_gen/runtime/entrypoints/openai/image_api.pydef _runtime_sampling_quality(quality: str | None) -> str | None:
    """把 OpenAI 的自动默认值挡在 SGLang 采样契约之外。    OpenAI 客户端常默认携带 quality=auto,原样透传会让服务端误以为
    用户显式选择档位并可能改变解码路径;这里统一映射为 None(不指定),
    由运行时保持默认(lossless 原始路径),只有显式的 high / lossless 等
    才进入采样契约。
    """
    return None if quality in (None, "auto") else quality
​
​
# 在 generations / edits 两个入口的 build_sampling_params( ... ) 调用中:
# quality 经 _runtime_sampling_quality 过滤后透传,edits 端点在表单上
# 新增了 quality: Optional[str] = Form(None) 参数。
# 注意与 output_quality(仅控制输出文件压缩)语义不同、互不覆盖。
python/sglang/multimodal_gen/runtime/pipelines_core/stages/decoding.py core-logic

DecodingStage.forward 由手写开关改为 use_vae_fast_path 上下文管理器,是门控语义落地的关键接线点。

# python/sglang/multimodal_gen/runtime/pipelines_core/stages/decoding.py
# DecodingStage.forward 中的核心解码段(节选)with self.use_declared_component(
    component_name=self.component_name,
    module=self.vae,
) as vae:
    assert vae is not None
    self.vae = vae
​
    # 统一的 decode 作用域门控:quality == "high" 才走 CUDA 融合快速路径,
    # 其余取值(None / "lossless" 等)保持原始模块路径逐比特一致。
    # use_vae_fast_path 内部用 try/finally 保证 decode 抛异常时也会复位开关。
    with use_vae_fast_path(vae, batch.sampling_params.quality == "high"):
        frames = self.decode(batch.latents, server_args, vae_dtype=vae_dtype)
​
        # 需要时批量解码 trajectory latents 以提高 GPU 利用率:
        # 先展平成 [B*T, C, F, H, W] 一次调核,再按时间步切回列表
        if batch.return_trajectory_decoded:
            assert (
                batch.trajectory_latents is not None
            ), "batch should have trajectory latents"
​
            B, T, C, F, H, W = batch.trajectory_latents.shape
            flat_latents = batch.trajectory_latents.view(B * T, C, F, H, W)
            all_decoded = self.decode(
                flat_latents, server_args, vae_dtype=vae_dtype
            )
            decoded_tensor = all_decoded.view(B, T, *all_decoded.shape[1:])
            trajectory_decoded = [decoded_tensor[:, i] for i in range(T)]
        else:
            trajectory_decoded = None

评论区精华

无实质 review 讨论,仅有 CI 重跑指令 other

PR 的 3 条评论均为作者 mickqian 触发的 /tag-and-rerun-ci 与 /tag-and-rerun-ci extra,无 reviewer 评论。设计权衡(按 quality 显式门控、安装 fail-closed、lossless 逐比特一致)在前置 PR #33818 中已有铺垫。

结论:作者自审自合,无遗留待解决项。 · closed

风险与影响

  1. 属性访问回归风险decoding.pygetattr(batch.sampling_params, "quality", None) 改为直接 batch.sampling_params.quality,若存在未设置 quality 字段的 sampling_params 构造路径,会抛 AttributeError 使 VAE decode 直接失败,需确认 build_sampling_params 全路径均带默认值。
  2. 对象身份匹配风险:注册表按 nn.Module 对象身份查找,DecodingStage.forward 传入的 vae 若与安装时注册的不是同一实例(如组件管理器按需加载或包装返回新对象),快速路径会静默关闭——fail-closed 保证正确性,但可能静默损失性能。
  3. 全局门控并发语义:gate 是每 VAE 一份的全局可变状态,use_vae_fast_path 只保证同线程内嵌套正确;若多个请求并发 decode 同一 VAE 且 quality 不同,存在状态互相覆盖的竞态(运行时若串行化 decode 则无碍)。
  4. 取值校验缺失video_api.py 对 quality 无白名单校验,未知取值原样透传,最终仅在等于 "high" 时生效从而被静默忽略;OpenAI 标准的 quality="low" 也会被透传,行为未定义。
  5. 回归防线未变:快速路径安装仍 fail-closed(Triton 缺失、attention 块不兼容、spatial-parallel decode 时跳过),本 PR 未削弱该防线。

用户侧:图像 generations/edits 与视频请求现在可显式传 quality="high" 获得加速 VAE 解码(#33818 报告 AutoencoderKL 解码约 2.4x 提速);不传或传 "auto" 时行为与之前完全一致。系统侧:门控实现从两处复制收敛为单一模块,wan_vae_cuda_opt 不再反向依赖 flux2_vae_cuda_opt,依赖方向更清晰;decode 开关由上下文管理器接管,异常路径更安全。团队侧:新增 3 处单测覆盖与 1 处文档说明,后续新增 VAE 快速路径只需注册 gate 即可复用同一套门控语义。影响范围限定在 multimodal_gen diffusion 运行时,不触及 SRT 核心调度。

直接属性访问无 getattr 兜底 门控注册按对象身份匹配 全局门控状态依赖串行 decode quality 取值无服务端校验

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论