Prhub

#34243 [Diffusion] Serve Cosmos3 policies through the Action API

原始 PR 作者 mickqian 合并时间 2026-08-10 18:16 文件变更 21 提交数 5 评论 1 代码增减 +608 / -92

执行摘要

Cosmos3 动作模式迁移至通用 Action API

PR body 明确说明:Cosmos3 policy 和 inverse-dynamics 模式产生连续机器人动作,但此前通过异步 /v1/videos API 暴露,导致响应契约依赖视觉任务,且即使主要结果是动作张量,也会不必要地进入视频输出处理。因此需要将动作产出的模式迁移到 SGLang 通用 Action API,同时保留 Cosmos3 的视觉生成能力(forward_dynamics 仍走 /v1/videos)。

值得精读。三个看点:① protocol.py 的 Cosmos3 特化分支(capabilities 位 + 请求降级函数)展示了如何在统一 TI2V 管线上叠加通用 Action API;② Cosmos3DecodingStage 的 ACTION 短路设计避免动作请求为视频产物付费;③ vla→action 全局重命名对模块边界的整理。若你在维护对外 API 或计划接入新动作产出模型,可复用这套模式。

讨论亮点

该 PR 没有外部 review 评论,唯一一条评论是作者 mickqian 触发 CI 的 /tag-and-rerun-ci。没有可提炼的公开设计讨论;实现权衡体现在 5 个提交的演进顺序中:先落地功能,随后修复内存观测处理、跳过动作输出文件名、接受 ActionSamplingParams,最后把 vla 模块整体改名为 action 对齐模块属主。

实现拆解

  1. 端点能力抽象与路由:在 configs/pipeline_configs/base.py 新增 supports_action_endpoint()supports_openpi_endpoint() 能力位;Cosmos3Config 覆盖 supports_action_endpointPi05PipelineConfig 覆盖 supports_openpi_endpointhttp_server.py 据此挂载 action_api.routeropenpi.router,替代原先基于 task_type.is_action_gen() 的判断。
  2. Action 协议泛化与 Cosmos3 特化runtime/entrypoints/vla/ 整体重命名为 runtime/entrypoints/action/protocol.py 将采样参数类解析放宽为 SamplingParams | ActionSamplingParams,新增 _build_cosmos3_action_sampling_params_cosmos3_image_from_observation,把图像/视频观测、action_horizondomain_name 等参数下沉为 Cosmos3SamplingParamsaction_metadataCosmos3Config 输出独立契约(modalities、supported_resolutions、action_modes、capabilities 等)。
  3. 采样参数与解码行为configs/sample/cosmos3.py_adjust 根据 action_mode 校验并将 policy / inverse_dynamics 请求置为 DataType.ACTION,同时关闭 save_outputreturn_file_paths_onlyreturn_frames 等视觉产物;Cosmos3DecodingStage.forwardDataType.ACTION 时直接返回动作张量并携带 domain_idraw_action_dim 元数据,绕过 VAE decode 与 guardrails;Cosmos3LatentPreparationStage 也允许 ACTION 类型复用图像/视频条件 latent。
  4. 视频入口保护runtime/entrypoints/openai/video_api.py_build_video_sampling_params 在构造出 DataType.ACTION 的采样参数时抛 ValueError,由 HTTP 层转为 400,提示客户端改用 /v1/actions/generations
  5. 测试与文档配套test/unit/test_cosmos3.py 新增 TestCosmos3ActionEndpoint(请求降级、metadata、响应格式、forward_dynamics 拒绝、action-only decode)与采样参数 _adjust 行为测试;docs/cookbook/diffusion/Cosmos/Cosmos3.mdx 更新为规范 Action API 示例与三种模式的路由说明。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/entrypoints/action/protocol.py 动作接口 renamed 9.08
python/sglang/multimodal_gen/test/unit/test_cosmos3.py 单元测试 modified 7.42
python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/cosmos3.py 解码阶段 modified 7.58
python/sglang/multimodal_gen/configs/sample/action.py 采样参数 renamed 7.12
python/sglang/multimodal_gen/configs/sample/cosmos3.py 采样参数 modified 6.97
python/sglang/multimodal_gen/runtime/entrypoints/openai/video_api.py 视频接口 modified 6.35
python/sglang/multimodal_gen/configs/pipeline_configs/base.py 管线配置 modified 6.2

关键符号

build_action_sampling_params _build_cosmos3_action_sampling_params _cosmos3_image_from_observation action_metadata Cosmos3SamplingParams._adjust Cosmos3DecodingStage.forward supports_action_endpoint supports_openpi_endpoint _build_video_sampling_params

关键源码片段

python/sglang/multimodal_gen/runtime/entrypoints/action/protocol.py rename-or-move

Action API 协议核心:从 vla/protocol.py 重命名而来,新增 Cosmos3 特化的 action_metadata 契约与 _build_cosmos3_action_sampling_params 请求降级路径,是所有 Action API 请求进入 Cosmos3 采样的枢纽。

def action_metadata(server_args: ServerArgs) -> dict[str, Any]:
    pipeline_config = server_args.pipeline_config
    # Cosmos3 是统一的 TI2V 视频管线,但 policy / inverse_dynamics
    # 模式以动作张量为输出,因此需要一份独立的 Action 契约描述
    if isinstance(pipeline_config, Cosmos3Config):
        defaults = Cosmos3SamplingParams()
        return {
            "object": "action.metadata",
            "model": server_args.model_id or server_args.model_path,
            "model_path": server_args.model_path,
            "policy_family": "cosmos3",
            "input": {
                # 支持 image 与 video 两种观测输入,分辨率来自采样参数默认值
                "modalities": ["image", "video"],
                "supported_resolutions": [
                    list(resolution) for resolution in defaults.supported_resolutions
                ],
                "state_dim": None,
            },
            "output": {
                # 动作输出为连续张量;action_dim 由具体 checkpoint 决定,
                # 这里只暴露 padded_action_dim 保证 metadata 稳定
                "action_type": "continuous",
                "action_horizon": 16,
                "action_dim": None,
                "padded_action_dim": pipeline_config.dit_config.arch_config.action_dim,
                "dtype": "float32",
            },
            "runtime": {
                "parallelism": {
                    "num_gpus": server_args.num_gpus,
                    "tp_size": server_args.tp_size,
                    "sp_degree": server_args.sp_degree,
                    "ulysses_degree": server_args.ulysses_degree,
                    "ring_degree": server_args.ring_degree,
                }
            },
            "defaults": {
                # 默认走 policy 模式,其余生成参数与配置默认值保持一致
                "action_mode": "policy",
                "action_horizon": 16,
                "num_inference_steps": defaults.num_inference_steps,
                "height": 480,
                "width": 832,
                "fps": 5,
            },
            "capabilities": {
                # Cosmos3 只暴露通用 Action API,不兼容 OpenPI websocket
                "action_modes": ["policy", "inverse_dynamics"],
                "realtime_websocket": True,
                "openpi_websocket": False,
                "batch_inputs": False,
                "multiple_candidates": False,
            },
        }
​
    # 以下为通用动作模型的 metadata 分支(policy_family 推导),
    # 字段结构与此前 vla/protocol.py 的行为保持一致
    policy_family = getattr(
        pipeline_config,
        "policy_family",
        type(pipeline_config).__name__.removesuffix("PipelineConfig").lower(),
    )
    return {
        "object": "action.metadata",
        # ... 通用分支其余字段与原逻辑一致
    }
python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/cosmos3.py data-contract

Cosmos3DecodingStage 是动作输出的关键行为变更:DataType.ACTION 时直接返回动作张量,跳过 VAE decode 与 guardrails;LatentPreparation 也允许 ACTION 类型复用视觉条件。

def forward(self, batch: Req, server_args: ServerArgs):
    """Decode latents to video, or short-circuit to raw actions for ACTION requests."""
    from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import OutputBatch
​
    # 首先统一提取动作预测:裁剪到真实动作维度并按需反归一化
    action_pred = None
    if getattr(batch, "action_latents", None) is not None:
        raw_action_dim = batch.extra.get("raw_action_dim")
        action_pred = batch.action_latents.float().cpu()
        if raw_action_dim is not None:
            action_pred = action_pred[:, :, :raw_action_dim]
        stats_path = getattr(batch.sampling_params, "action_stats_path", None)
        if stats_path is not None:
            method = getattr(batch.sampling_params, "action_normalization", "quantile")
            action_pred = denormalize_action(
                action_pred, method, load_action_stats(stats_path)
            )
        self.log_info(f"Action predictions shape: {tuple(action_pred.shape)}")
​
    # 从 batch.extra 中恢复动作域信息,供 response envelope 使用
    action_domain_ids = batch.extra.get("action_domain_ids")
    action_domain_id = (
        int(action_domain_ids[0].item()) if action_domain_ids is not None else None
    )
    action_metadata = {
        "action_mode": getattr(batch.sampling_params, "action_mode", None),
        "action_domain_id": action_domain_id,
        "action_raw_action_dim": (
            batch.extra.get("raw_action_dim")
            if getattr(batch, "extra", None)
            else None
        ),
    }
​
    if batch.data_type == DataType.ACTION:
        # 动作请求直接返回动作张量,跳过视频 VAE decode、guardrails 与媒体序列化
        if action_pred is None:
            raise RuntimeError("Cosmos3 action request produced no action tensor")
        payload = {
            "request_id": batch.request_id,
            "actions": action_pred[0].numpy(),
            "action_mode": action_metadata["action_mode"],
            "domain_id": action_metadata["action_domain_id"],
            "raw_action_dim": action_metadata["action_raw_action_dim"],
            "parameters": {
                "num_inference_steps": batch.num_inference_steps,
                "num_frames": batch.num_frames,
            },
        }
        return OutputBatch(
            output=[payload],
            action_pred=action_pred,
            metrics=batch.metrics if hasattr(batch, "metrics") else None,
            **action_metadata,
        )
​
    # 视觉输出路径(IMAGE / VIDEO)保持原有 VAE 解码与 guardrails 流程
    is_image_gen = batch.data_type == DataType.IMAGE
    self.log_info(
        "Decoding latents to image..." if is_image_gen else "Decoding latents to video..."
    )
python/sglang/multimodal_gen/configs/sample/cosmos3.py core-logic

_adjust 根据 action_mode 判定是否输出 DataType.ACTION 并关闭视觉产物,是 action 输出判定的核心决策点。

def _adjust(self, server_args) -> None:
    # 先判定请求是否产出动作:policy / inverse_dynamics 输出连续动作,
    # forward_dynamics 以动作为输入产出视频,仍属于视觉请求
    action_output = False
    if self.action_mode is not None:
        self.action_mode = str(self.action_mode).strip().lower()
        if self.action_mode not in (
            "policy",
            "forward_dynamics",
            "inverse_dynamics",
        ):
            raise ValueError(
                f"Unsupported action_mode={self.action_mode!r}; expected "
                "'policy', 'forward_dynamics', or 'inverse_dynamics'."
            )
        action_output = self.action_mode != "forward_dynamics"
​
    # 先执行基类的视觉路径调整(数据增强、路径展开等)
    super()._adjust(server_args)
​
    # 动作输出不需要任何视觉产物:关闭保存、文件路径、逐帧返回与压缩
    if action_output:
        self.data_type = DataType.ACTION
        self.save_output = False
        self.return_file_paths_only = False
        self.return_frames = False
        self.output_file_name = None
        self.output_compression = 0
​
​
def _set_output_file_name(self) -> None:
    # 动作输出永远不需要视觉文件名,提前返回以避免对内存中的观测图做 hashing
    if self.action_mode in ("policy", "inverse_dynamics"):
        return
    # 单帧请求按 T2I 处理,派生图片扩展名;其余走视频扩展名
    if self.num_frames == 1:
        self.data_type = DataType.IMAGE
    super()._set_output_file_name()

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

1) 跨模块重命名:vla 包整体改名为 action(api.py、protocol.py、openpi.py、ws_utils.py、init.py),若外部代码直接 import sglang.multimodal_gen.runtime.entrypoints.vla 会失效,仓库内部引用已全部替换但缺少兼容 alias。
2) 解码路径重构:Cosmos3DecodingStage.forward 把 action 提取逻辑前移并新增 ACTION 短路,视频/图像生成路径存在回归风险;单元测试用 FailIfDecoded 守护了 ACTION 分支,但没有覆盖视觉路径的端到端回归。
3) 入口契约变化:video_api 对 DataType.ACTION 抛错,若其他 pipeline 的采样参数被误标为 ACTION 会拒绝正常视频请求,目前仅 Cosmos3 会设置该类型,影响面有限。
4) 硬编码默认值:protocol.py 中 Cosmos3 分支硬编码 action_horizon=16、height=480、width=832、fps=5,若模型配置变更需同步维护。
5) CI 状态:PR Test (Extra) 显示失败(作者已触发 /tag-and-rerun-ci),最终 CI 结论需以重跑结果为准。

用户侧:Cosmos3 policy / inverse_dynamics 消费者获得标准 /v1/actions/generations 端点,响应为连续动作张量及 domain 元数据;继续向 /v1/videos 提交动作请求会收到 400 与提示。系统侧:动作请求在解码阶段短路,跳过 VAE decode 与 guardrails,减少不必要的视觉后处理。代码库侧:vla 模块改名 action,统一动作类模型命名;pipeline 基类新增能力位,未来新模型接入 Action API 成本降低。影响范围限于 multimodal_gen 扩散子系统,不影响 sglang/srt 核心推理路径。

跨模块重命名 入口契约变更 解码路径重构 硬编码默认值 CI 待复跑

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论