Add Cosmos3 Edge and Distilled checkpoints support (#5001)

Signed-off-by: Bartosz Stefaniak <bstefaniak@nvidia.com>
This commit is contained in:
bastefaniak authored and GitHub committed 2026-07-16 09:39:40 -07:00
1 parent 0a9a5c231e
commit 85f7a5b374
12 files changed
+1789 -216

No files matched your search

@@ -36,20 +36,36 @@ class StubScheduler:
timesteps: list[int] | None = None,
*,
flow_shift: float = 1.0,
use_karras_sigmas: bool = True,
) -> None:
self.timesteps = torch.tensor(timesteps or [9, 3], dtype=torch.int64)
self.config = SimpleNamespace(
num_train_timesteps=1000,
flow_shift=flow_shift,
use_karras_sigmas=use_karras_sigmas,
)
self.set_timesteps_calls: list[tuple[int, torch.device]] = []
self.set_timesteps_calls: list[dict[str, Any]] = []
self.step_calls: list[tuple[torch.Tensor, torch.Tensor, torch.Tensor]] = []
def set_timesteps(self, num_steps: int, device: torch.device) -> None:
self.set_timesteps_calls.append((num_steps, device))
self.timesteps = torch.arange(num_steps, 0, -1, dtype=torch.int64, device=device)
def set_timesteps(
self,
num_inference_steps: int | None = None,
device: str | torch.device | None = None,
*,
shift: float | None = None,
sigmas: list[float] | None = None,
) -> None:
self.set_timesteps_calls.append(
{
"num_inference_steps": num_inference_steps,
"device": device,
"shift": shift,
"sigmas": sigmas,
}
)
if sigmas is not None:
self.timesteps = torch.tensor(sigmas, device=device)
else:
assert num_inference_steps is not None
self.timesteps = torch.arange(num_inference_steps, 0, -1, dtype=torch.int64, device=device)
def step(self, noise_pred: torch.Tensor, timestep: torch.Tensor, latents: torch.Tensor, **kwargs):
del kwargs
@@ -200,6 +216,7 @@ def fake_cosmos3_guardrails(monkeypatch: pytest.MonkeyPatch):
def make_cosmos3_pipeline():
def _make():
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import (
COSMOS3_VIDEO_DEFAULT_FLOW_SHIFT,
Cosmos3OmniDiffusersPipeline,
)
@@ -213,11 +230,10 @@ def make_cosmos3_pipeline():
pipeline.vae_scale_factor_temporal = 4
pipeline.vae_scale_factor_spatial = 8
pipeline.scheduler = StubScheduler([9, 3], flow_shift=1.0)
pipeline._base_scheduler_config = pipeline.scheduler.config
pipeline._engine_init_flow_shift = 1.0
pipeline._current_flow_shift = 1.0
pipeline._base_scheduler_use_karras_sigmas = True
pipeline._current_scheduler_use_karras_sigmas = True
pipeline._engine_init_flow_shift = COSMOS3_VIDEO_DEFAULT_FLOW_SHIFT
pipeline._current_flow_shift = COSMOS3_VIDEO_DEFAULT_FLOW_SHIFT
pipeline.is_distilled_model = False
pipeline.is_edge_model = False
pipeline._guidance_scale = None
pipeline._num_timesteps = None
pipeline._cosmos3_branch_caches = None
@@ -243,6 +259,7 @@ def make_sampling_params(**overrides: Any) -> SimpleNamespace:
"num_frames": None,
"num_inference_steps": None,
"guidance_scale": None,
"guidance_scale_provided": False,
"generator": None,
"seed": 123,
"num_outputs_per_prompt": 1,
@@ -288,6 +305,165 @@ def _mask() -> torch.Tensor:
return torch.ones(1, 1, dtype=torch.long)
def _capture_tokenize_calls(pipeline: Any) -> list[dict[str, Any]]:
calls: list[dict[str, Any]] = []
def _tokenize(
text: str,
max_sequence_length: int,
use_system_prompt: bool = False,
system_prompt: str | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
index = len(calls) + 1
calls.append(
{
"text": text,
"max_sequence_length": max_sequence_length,
"use_system_prompt": use_system_prompt,
"system_prompt": system_prompt,
}
)
return _ids(index), torch.full((1, 1), index, dtype=torch.long)
pipeline._tokenize_prompt = _tokenize
return calls
@pytest.mark.parametrize(
("provided", "value", "default", "is_distilled", "expected"),
[
(False, 1.0, 7.0, False, 7.0),
(True, 1.0, 7.0, False, 1.0),
(True, 4.5, 7.0, False, 4.5),
(False, 1.0, 7.0, True, 1.0),
(True, 4.5, 7.0, True, 1.0),
],
)
def test_resolve_guidance_scale(
make_cosmos3_pipeline,
provided: bool,
value: float,
default: float,
is_distilled: bool,
expected: float,
) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_distilled_model = is_distilled
sp = make_sampling_params(
guidance_scale=value,
guidance_scale_provided=provided,
)
assert pipeline._resolve_guidance_scale(sp, default) == expected
def test_distilled_generation_accepts_t2i_and_i2v(make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_distilled_model = True
common = {
"action_enabled": False,
"transfer_config": None,
"is_v2v": False,
"sound_enabled": False,
}
pipeline._validate_distilled_generation_mode(
is_t2i=True,
image_tensor=None,
**common,
)
pipeline._validate_distilled_generation_mode(
is_t2i=False,
image_tensor=torch.zeros(1),
**common,
)
@pytest.mark.parametrize(
("overrides", "message"),
[
({"action_enabled": True}, "action requests are unsupported"),
({"transfer_config": SimpleNamespace()}, "transfer requests are unsupported"),
({"is_v2v": True}, "video-to-video requests are unsupported"),
({"sound_enabled": True}, "sound generation is unsupported"),
(
{"is_t2i": False, "image_tensor": None},
"text-to-video requests are unsupported",
),
(
{"is_t2i": True, "image_tensor": torch.zeros(1)},
"image-conditioned image generation is unsupported",
),
],
)
def test_distilled_generation_rejects_unsupported_modes(
make_cosmos3_pipeline,
overrides: dict[str, Any],
message: str,
) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_distilled_model = True
kwargs = {
"is_t2i": True,
"image_tensor": None,
"action_enabled": False,
"transfer_config": None,
"is_v2v": False,
"sound_enabled": False,
}
kwargs.update(overrides)
with pytest.raises(ValueError, match=message):
pipeline._validate_distilled_generation_mode(**kwargs)
def test_distilled_generation_rejects_robolab_policy(make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_distilled_model = True
with pytest.raises(ValueError, match="RoboLab/action policy requests are unsupported"):
pipeline._forward_robolab_policy(make_sampling_params(), None, 0.0)
@pytest.mark.parametrize(
("prompt", "sampling_params", "message"),
[
(
{"prompt": "x", "modalities": ["video"], "generate_sound": True},
make_sampling_params(),
"do not support sound generation",
),
(
{"prompt": "x", "modalities": ["video"]},
make_sampling_params(extra_args={"edge": {"control_path": "/tmp/control.mp4"}}),
"do not support transfer inference",
),
(
{
"prompt": "x",
"modalities": ["video"],
"additional_information": {
"preprocessed_video": torch.zeros(1, 3, 5, 16, 16),
},
},
make_sampling_params(height=16, width=16, num_frames=5),
"do not support video-to-video generation",
),
],
)
def test_edge_forward_rejects_unsupported_modes(
make_cosmos3_pipeline,
prompt: dict[str, Any],
sampling_params: SimpleNamespace,
message: str,
) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_edge_model = True
with pytest.raises(ValueError, match=message):
pipeline.forward(SimpleNamespace(prompts=[prompt], sampling_params=sampling_params))
def test_pipeline_registered_and_exported() -> None:
from vllm_omni.diffusion.cache.cache_dit_backend import CUSTOM_DIT_ENABLERS
from vllm_omni.diffusion.models import cosmos3
@@ -303,17 +479,45 @@ def test_pipeline_registered_and_exported() -> None:
assert issubclass(Cosmos3OmniDiffusersPipeline, nn.Module)
assert issubclass(Cosmos3OmniDiffusersPipeline, ProgressBarMixin)
assert Cosmos3OmniDiffusersPipeline.support_image_input is True
assert _DIFFUSION_MODELS["Cosmos3OmniDiffusersPipeline"] == (
"cosmos3",
"pipeline_cosmos3",
"Cosmos3OmniDiffusersPipeline",
)
assert _DIFFUSION_PRE_PROCESS_FUNCS["Cosmos3OmniDiffusersPipeline"] == "get_cosmos3_pre_process_func"
assert _DIFFUSION_POST_PROCESS_FUNCS["Cosmos3OmniDiffusersPipeline"] == "get_cosmos3_post_process_func"
assert _DIFFUSION_IR_OP_PRIORITY_FUNCS["Cosmos3OmniDiffusersPipeline"] == "get_cosmos3_ir_op_priority_func"
assert "Cosmos3OmniDiffusersPipeline" in CUSTOM_DIT_ENABLERS
assert "Cosmos3OmniDiffusersPipeline" in cosmos3.__all__
for pipeline_name in ("Cosmos3OmniDiffusersPipeline", "Cosmos3OmniPipeline"):
assert _DIFFUSION_MODELS[pipeline_name] == (
"cosmos3",
"pipeline_cosmos3",
"Cosmos3OmniDiffusersPipeline",
)
assert _DIFFUSION_PRE_PROCESS_FUNCS[pipeline_name] == "get_cosmos3_pre_process_func"
assert _DIFFUSION_POST_PROCESS_FUNCS[pipeline_name] == "get_cosmos3_post_process_func"
assert _DIFFUSION_IR_OP_PRIORITY_FUNCS[pipeline_name] == "get_cosmos3_ir_op_priority_func"
assert pipeline_name in CUSTOM_DIT_ENABLERS
@pytest.mark.parametrize(
"pipeline_name",
["Cosmos3OmniDiffusersPipeline", "Cosmos3OmniPipeline"],
)
def test_cosmos3_model_index_resolves_pipeline(
tmp_path,
pipeline_name: str,
) -> None:
import json
from vllm_omni.diffusion.data import OmniDiffusionConfig
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import (
Cosmos3OmniDiffusersPipeline,
)
from vllm_omni.diffusion.registry import DiffusionModelRegistry
(tmp_path / "model_index.json").write_text(json.dumps({"_class_name": pipeline_name}))
config = OmniDiffusionConfig(model=str(tmp_path))
config.enrich_config()
assert config.model_class_name == pipeline_name
resolved_pipeline_cls = DiffusionModelRegistry._try_load_model_cls(config.model_class_name)
assert resolved_pipeline_cls is Cosmos3OmniDiffusersPipeline
@pytest.fixture
def stub_real_pipeline_init(monkeypatch: pytest.MonkeyPatch):
@@ -335,25 +539,24 @@ def stub_real_pipeline_init(monkeypatch: pytest.MonkeyPatch):
return self
class _StubDiffusersScheduler:
load_config_calls: list[dict[str, Any]] = []
from_config_calls: list[dict[str, Any]] = []
def __init__(self, *, flow_shift: float = 1.0, use_karras_sigmas: bool = True) -> None:
self.config = SimpleNamespace(flow_shift=flow_shift, use_karras_sigmas=use_karras_sigmas)
def __init__(self, *, flow_shift: float = 1.0) -> None:
self.config = SimpleNamespace(flow_shift=flow_shift)
@classmethod
def from_pretrained(cls, *args, **kwargs):
return cls()
def load_config(cls, *args, **kwargs):
cls.load_config_calls.append({"args": args, "kwargs": dict(kwargs)})
return {
"_class_name": "UniPCMultistepScheduler",
"flow_shift": 1.0,
}
@classmethod
def from_config(cls, config, **kwargs):
cls.from_config_calls.append({"config": config, "kwargs": dict(kwargs)})
return cls(
flow_shift=kwargs.get("flow_shift", getattr(config, "flow_shift", 1.0)),
use_karras_sigmas=kwargs.get(
"use_karras_sigmas",
getattr(config, "use_karras_sigmas", True),
),
)
return cls(flow_shift=kwargs.get("shift", config.get("flow_shift", 1.0)))
class _StubVideoProcessor:
def __init__(self, *args, **kwargs) -> None:
@@ -361,13 +564,18 @@ def stub_real_pipeline_init(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(pipeline_cosmos3, "AutoTokenizer", _StubAutoTokenizer)
monkeypatch.setattr(pipeline_cosmos3, "DistributedAutoencoderKLWan", _StubDiffusersVAE)
monkeypatch.setattr(pipeline_cosmos3, "UniPCMultistepScheduler", _StubDiffusersScheduler)
monkeypatch.setattr(pipeline_cosmos3, "FlowUniPCMultistepScheduler", _StubDiffusersScheduler)
monkeypatch.setattr(pipeline_cosmos3, "VideoProcessor", _StubVideoProcessor)
monkeypatch.setattr(pipeline_cosmos3, "get_local_device", lambda: torch.device("cpu"))
return _StubDiffusersScheduler
def _make_od_config(*, sound_gen: bool) -> SimpleNamespace:
def _make_od_config(
*,
sound_gen: bool,
tf_model_config_overrides: dict[str, Any] | None = None,
model_config: dict[str, Any] | None = None,
) -> SimpleNamespace:
tf_model_config = {
"hidden_size": 8,
"num_hidden_layers": 0,
@@ -382,6 +590,8 @@ def _make_od_config(*, sound_gen: bool) -> SimpleNamespace:
}
if sound_gen:
tf_model_config["sound_gen"] = True
if tf_model_config_overrides:
tf_model_config.update(tf_model_config_overrides)
return SimpleNamespace(
enable_cpu_offload=False,
enable_diffusion_pipeline_profiler=False,
@@ -390,7 +600,7 @@ def _make_od_config(*, sound_gen: bool) -> SimpleNamespace:
flow_shift=None,
quantization_config=None,
custom_pipeline_args={},
model_config={},
model_config=model_config or {},
tf_model_config=tf_model_config,
)
@@ -400,16 +610,13 @@ def test_pipeline_init_skips_tokenizer_when_sound_disabled(stub_real_pipeline_in
pipeline = Cosmos3OmniDiffusersPipeline(od_config=_make_od_config(sound_gen=False))
assert stub_real_pipeline_init.from_config_calls == []
assert pipeline._sound_tokenizer is None
assert pipeline.transformer.sound_gen is False
assert not hasattr(pipeline.transformer, "audio_proj_in")
assert not hasattr(pipeline.transformer, "audio_proj_out")
assert pipeline._base_scheduler_use_karras_sigmas is True
assert pipeline._current_scheduler_use_karras_sigmas is True
def test_pipeline_init_rebuilds_scheduler_with_cosmos3_defaults(stub_real_pipeline_init) -> None:
def test_pipeline_init_uses_flow_unipc_with_cosmos3_defaults(stub_real_pipeline_init) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import Cosmos3OmniDiffusersPipeline
od_config = _make_od_config(sound_gen=False)
@@ -417,55 +624,302 @@ def test_pipeline_init_rebuilds_scheduler_with_cosmos3_defaults(stub_real_pipeli
pipeline = Cosmos3OmniDiffusersPipeline(od_config=od_config)
assert stub_real_pipeline_init.from_config_calls
call = stub_real_pipeline_init.from_config_calls[-1]
assert getattr(call["config"], "flow_shift") == 1.0
assert call["kwargs"] == {"flow_shift": 2.5}
assert len(stub_real_pipeline_init.load_config_calls) == 1
assert len(stub_real_pipeline_init.from_config_calls) == 1
call = stub_real_pipeline_init.from_config_calls[0]
assert call["config"]["_class_name"] == "UniPCMultistepScheduler"
assert call["kwargs"] == {
"shift": 1.0,
"use_dynamic_shifting": False,
"prediction_type": "flow_prediction",
}
assert pipeline.is_distilled_model is False
assert pipeline._engine_init_flow_shift == 2.5
assert pipeline._base_scheduler_use_karras_sigmas is True
def test_set_flow_shift_restores_checkpoint_scheduler_mode(
make_cosmos3_pipeline,
@pytest.mark.parametrize(
("scheduler_class_name", "expected_distilled"),
[
("FlowMatchEulerDiscreteScheduler", True),
("UniPCMultistepScheduler", False),
],
)
def test_pipeline_resolves_scheduler_class_from_checkpoint_file(
tmp_path,
stub_real_pipeline_init,
monkeypatch: pytest.MonkeyPatch,
scheduler_class_name: str,
expected_distilled: bool,
) -> None:
import json
from vllm_omni.diffusion.models.cosmos3 import pipeline_cosmos3
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import Cosmos3OmniDiffusersPipeline
from vllm_omni.diffusion.models.schedulers.scheduling_flow_unipc_multistep import (
FlowUniPCMultistepScheduler as RealFlowUniPCMultistepScheduler,
)
class RecordingScheduler:
from_config_calls: list[dict[str, Any]] = []
t_list = [1.0, 0.75, 0.5, 0.25]
scheduler_dir = tmp_path / "scheduler"
scheduler_dir.mkdir()
scheduler_config = {"_class_name": scheduler_class_name}
if expected_distilled:
scheduler_config["fixed_step_sampler_config"] = {"sample_type": "sde", "t_list": t_list}
(scheduler_dir / "scheduler_config.json").write_text(json.dumps(scheduler_config))
def __init__(self, *, flow_shift: float, use_karras_sigmas: bool) -> None:
self.config = SimpleNamespace(flow_shift=flow_shift, use_karras_sigmas=use_karras_sigmas)
class StubFlowUniPCScheduler(StubScheduler):
from_config_calls: list[tuple[Any, dict[str, Any]]] = []
@classmethod
def load_config(cls, *args, **kwargs):
return RealFlowUniPCMultistepScheduler.load_config(*args, **kwargs)
@classmethod
def from_config(cls, config, **kwargs):
cls.from_config_calls.append({"config": config, "kwargs": dict(kwargs)})
return cls(
flow_shift=kwargs.get("flow_shift", getattr(config, "flow_shift", 1.0)),
use_karras_sigmas=kwargs.get(
"use_karras_sigmas",
getattr(config, "use_karras_sigmas", True),
),
)
cls.from_config_calls.append((config, dict(kwargs)))
return cls()
monkeypatch.setattr(pipeline_cosmos3, "UniPCMultistepScheduler", RecordingScheduler)
pipeline = make_cosmos3_pipeline()
class StubFlowMatchScheduler(StubScheduler):
from_config_calls: list[tuple[Any, dict[str, Any]]] = []
pipeline._set_flow_shift(10.0, use_karras_sigmas=False)
assert RecordingScheduler.from_config_calls[-1]["kwargs"] == {
"flow_shift": 10.0,
"use_karras_sigmas": False,
@classmethod
def from_config(cls, config, **kwargs):
cls.from_config_calls.append((config, dict(kwargs)))
scheduler = cls()
scheduler.config.fixed_step_sampler_config = config["fixed_step_sampler_config"]
return scheduler
monkeypatch.setattr(pipeline_cosmos3, "FlowUniPCMultistepScheduler", StubFlowUniPCScheduler)
monkeypatch.setattr(
pipeline_cosmos3,
"FlowMatchEulerDiscreteScheduler",
StubFlowMatchScheduler,
)
od_config = _make_od_config(sound_gen=False)
od_config.model = str(tmp_path)
pipeline = Cosmos3OmniDiffusersPipeline(od_config=od_config)
assert pipeline.is_distilled_model is expected_distilled
if expected_distilled:
assert len(StubFlowMatchScheduler.from_config_calls) == 1
assert StubFlowMatchScheduler.from_config_calls[0][1] == {"stochastic_sampling": True}
assert StubFlowUniPCScheduler.from_config_calls == []
assert pipeline._scheduler_init_t_list == t_list
else:
assert len(StubFlowUniPCScheduler.from_config_calls) == 1
assert StubFlowMatchScheduler.from_config_calls == []
assert not hasattr(pipeline, "_scheduler_init_t_list")
def test_distilled_pipeline_initializes_sde_scheduler(
stub_real_pipeline_init,
monkeypatch: pytest.MonkeyPatch,
) -> None:
from diffusers import FlowMatchEulerDiscreteScheduler
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import (
Cosmos3OmniDiffusersPipeline,
)
t_list = [1.0, 0.9375, 0.8333333333333334, 0.625]
scheduler_config = {
"_class_name": "FlowMatchEulerDiscreteScheduler",
"shift": 1.0,
"stochastic_sampling": False,
"fixed_step_sampler_config": {
"sample_type": "sde",
"t_list": t_list,
},
}
assert pipeline._current_flow_shift == 10.0
assert pipeline._current_scheduler_use_karras_sigmas is False
monkeypatch.setattr(
stub_real_pipeline_init,
"load_config",
classmethod(lambda cls, *args, **kwargs: scheduler_config),
)
pipeline._set_flow_shift(10.0)
assert RecordingScheduler.from_config_calls[-1]["kwargs"] == {"flow_shift": 10.0}
assert pipeline._current_flow_shift == 10.0
assert pipeline._current_scheduler_use_karras_sigmas is True
pipeline = Cosmos3OmniDiffusersPipeline(od_config=_make_od_config(sound_gen=False))
assert pipeline.is_distilled_model is True
assert isinstance(pipeline.scheduler, FlowMatchEulerDiscreteScheduler)
assert pipeline.scheduler.config.stochastic_sampling is True
assert pipeline._scheduler_init_t_list == t_list
pipeline._set_flow_shift(10.0)
assert len(RecordingScheduler.from_config_calls) == 2
@pytest.mark.parametrize(
("fixed_step_config", "error_pattern"),
[
pytest.param(
{"t_list": [1.0, 0.5]},
r"fixed_step_sampler_config\.sample_type=sde",
id="missing-sample-type",
),
pytest.param(
{"sample_type": "ode", "t_list": [1.0, 0.5]},
r"fixed_step_sampler_config\.sample_type=sde",
id="unsupported-sample-type",
),
pytest.param(
{"sample_type": "sde"},
r"non-empty fixed_step_sampler_config\.t_list",
id="missing-t-list",
),
pytest.param(
{"sample_type": "sde", "t_list": []},
r"non-empty fixed_step_sampler_config\.t_list",
id="empty-t-list",
),
],
)
def test_distilled_scheduler_validates_fixed_step_config(
stub_real_pipeline_init,
monkeypatch: pytest.MonkeyPatch,
fixed_step_config: dict[str, Any],
error_pattern: str,
) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import (
Cosmos3OmniDiffusersPipeline,
)
scheduler_config = {
"_class_name": "FlowMatchEulerDiscreteScheduler",
"fixed_step_sampler_config": fixed_step_config,
}
monkeypatch.setattr(
stub_real_pipeline_init,
"load_config",
classmethod(lambda cls, *args, **kwargs: scheduler_config),
)
with pytest.raises(ValueError, match=error_pattern):
Cosmos3OmniDiffusersPipeline(od_config=_make_od_config(sound_gen=False))
def test_pipeline_init_selects_edge_transformer_from_backbone_type(stub_real_pipeline_init) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import (
COSMOS3_EDGE_VIDEO_DEFAULT_FLOW_SHIFT,
Cosmos3OmniDiffusersPipeline,
)
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
COSMOS3_EDGE_BACKBONE_TYPE,
Cosmos3EdgeVFMTransformer,
)
pipeline = Cosmos3OmniDiffusersPipeline(
od_config=_make_od_config(
sound_gen=False,
tf_model_config_overrides={
"backbone_type": COSMOS3_EDGE_BACKBONE_TYPE,
"qk_norm_for_text": False,
"latent_channel": 48,
"latent_patch_size": 2,
"temporal_compression_factor": 4,
"layer_norm_epsilon": 1e-5,
},
)
)
assert isinstance(pipeline.transformer, Cosmos3EdgeVFMTransformer)
assert pipeline.is_edge_model is True
assert pipeline._engine_init_flow_shift == COSMOS3_EDGE_VIDEO_DEFAULT_FLOW_SHIFT
def test_pipeline_init_does_not_select_edge_from_model_index_class_name(stub_real_pipeline_init) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import Cosmos3OmniDiffusersPipeline
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import Cosmos3VFMTransformer
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import Cosmos3EdgeVFMTransformer
pipeline = Cosmos3OmniDiffusersPipeline(
od_config=_make_od_config(
sound_gen=False,
model_config={"_class_name": "Cosmos3EdgeOmniDiffusersPipeline"},
)
)
assert isinstance(pipeline.transformer, Cosmos3VFMTransformer)
assert not isinstance(pipeline.transformer, Cosmos3EdgeVFMTransformer)
def test_flow_unipc_reuses_scheduler_and_forwards_each_request_shift(make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
scheduler = pipeline.scheduler
assert pipeline._engine_init_flow_shift == 10.0
assert pipeline._current_flow_shift == 10.0
assert scheduler.config.flow_shift == 1.0
for shift in (3.0, 10.0):
pipeline._set_flow_shift(shift)
pipeline._set_timesteps(4, torch.device("cpu"), shift=pipeline._current_flow_shift)
assert pipeline.scheduler is scheduler
assert pipeline._current_flow_shift == 10.0
assert [call["shift"] for call in scheduler.set_timesteps_calls] == [3.0, 10.0]
assert all(call["num_inference_steps"] == 4 for call in scheduler.set_timesteps_calls)
assert all(call["sigmas"] is None for call in scheduler.set_timesteps_calls)
def test_flow_unipc_reproducible_with_same_seed(make_cosmos3_pipeline) -> None:
from vllm_omni.diffusion.models.schedulers.scheduling_flow_unipc_multistep import (
FlowUniPCMultistepScheduler,
)
pipeline = make_cosmos3_pipeline()
pipeline.scheduler = FlowUniPCMultistepScheduler(
shift=1.0,
use_dynamic_shifting=False,
prediction_type="flow_prediction",
)
pipeline._format_and_tokenize_prompts = lambda *args, **kwargs: (
_ids(1),
_mask(),
_ids(0),
_mask(),
)
def run(seed: int) -> torch.Tensor:
request = make_request_batch(
{"prompt": "A test video.", "modalities": ["video"]},
make_sampling_params(
seed=seed,
guidance_scale=1.0,
guidance_scale_provided=True,
num_inference_steps=4,
num_frames=5,
height=16,
width=16,
extra_args={"flow_shift": 3.0},
),
)
output = pipeline.forward(request)
return output.output["video"]
first = run(123)
second = run(123)
different_seed = run(456)
torch.testing.assert_close(first, second)
assert not torch.equal(first, different_seed)
def test_distilled_set_timesteps_uses_fixed_sigma_schedule(make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
pipeline.is_distilled_model = True
pipeline._scheduler_init_t_list = [1.0, 0.75, 0.5, 0.25]
pipeline._set_timesteps(
num_inference_steps=99,
device=torch.device("cpu"),
shift=7.0,
)
assert pipeline.scheduler.set_timesteps_calls == [
{
"num_inference_steps": None,
"device": torch.device("cpu"),
"shift": None,
"sigmas": pipeline._scheduler_init_t_list,
}
]
def test_pipeline_init_passes_tokenizer_attrs_into_transformer(
@@ -806,12 +1260,35 @@ def test_ir_op_priority_hook_preserves_platform_fields(monkeypatch: pytest.Monke
assert merged.custom_op == ["platform_kernel", "native"]
def test_prompt_formatting_and_checkpoint_key_remap(make_cosmos3_pipeline) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import Cosmos3OmniDiffusersPipeline
def test_format_and_tokenize_prompts_leaves_plain_prompts_unchanged_by_default(make_cosmos3_pipeline) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import COSMOS3_SYSTEM_PROMPT
pipeline = make_cosmos3_pipeline()
captured: list[str] = []
pipeline._tokenize_prompt = lambda text, *args, **kwargs: (captured.append(text) or _ids(len(captured)), _mask())
calls = _capture_tokenize_calls(pipeline)
result = pipeline._format_and_tokenize_prompts(
" A robot. ",
" bad. ",
num_frames=48,
frame_rate=24,
height=720,
width=1280,
max_sequence_length=32,
sp=SimpleNamespace(extra_args={}),
use_system_prompt=False,
is_t2i=False,
)
assert [call["text"] for call in calls] == ["A robot.", "bad."]
assert all(call["max_sequence_length"] == 32 for call in calls)
assert all(call["use_system_prompt"] is False for call in calls)
assert all(call["system_prompt"] == COSMOS3_SYSTEM_PROMPT for call in calls)
assert [tensor.item() for tensor in result] == [1, 1, 2, 2]
def test_format_and_tokenize_prompts_applies_video_templates_and_system_override(make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
calls = _capture_tokenize_calls(pipeline)
pipeline._format_and_tokenize_prompts(
"A robot",
@@ -821,21 +1298,167 @@ def test_prompt_formatting_and_checkpoint_key_remap(make_cosmos3_pipeline) -> No
height=720,
width=1280,
max_sequence_length=32,
sp=SimpleNamespace(
system_prompt="direct system prompt",
extra_args={
"use_duration_template": True,
"use_resolution_template": True,
"system_prompt": "API system prompt",
},
),
use_system_prompt=True,
is_t2i=False,
)
assert calls == [
{
"text": ("A robot. The video is 2.0 seconds long and is of 24 FPS. This video is of 720x1280 resolution."),
"max_sequence_length": 32,
"use_system_prompt": True,
"system_prompt": "API system prompt",
},
{
"text": (
"bad. The video is not 2.0 seconds long and is not of 24 FPS. This video is not of 720x1280 resolution."
),
"max_sequence_length": 32,
"use_system_prompt": True,
"system_prompt": "API system prompt",
},
]
def test_format_and_tokenize_prompts_uses_image_templates_for_t2i(make_cosmos3_pipeline) -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import COSMOS3_T2I_SYSTEM_PROMPT
pipeline = make_cosmos3_pipeline()
calls = _capture_tokenize_calls(pipeline)
pipeline._format_and_tokenize_prompts(
"A robot",
"bad",
num_frames=1,
frame_rate=24,
height=1024,
width=768,
max_sequence_length=64,
sp=SimpleNamespace(
extra_args={
"negative_metadata_mode": "inverse",
# Duration/resolution metadata templates are off by default
# (commit "Change resolution and duration templates to off by
# default"); enable them explicitly to exercise the formatting.
"use_duration_template": True,
"use_resolution_template": True,
}
),
use_system_prompt=True,
is_t2i=True,
)
assert [call["text"] for call in calls] == [
"A robot. This image is of 1024x768 resolution.",
"bad. This image is not of 1024x768 resolution.",
]
assert all("seconds" not in call["text"] and "FPS" not in call["text"] for call in calls)
assert all(call["system_prompt"] == COSMOS3_T2I_SYSTEM_PROMPT for call in calls)
def test_format_and_tokenize_prompts_rewrites_json_object_metadata(make_cosmos3_pipeline) -> None:
import json
pipeline = make_cosmos3_pipeline()
calls = _capture_tokenize_calls(pipeline)
pipeline._format_and_tokenize_prompts(
('{"caption": "A robot", "duration": "old", "fps": 1, "resolution": {"H": 1, "W": 2}, "aspect_ratio": "1:1"}'),
"bad",
num_frames=48,
frame_rate=24,
height=720,
width=1280,
max_sequence_length=32,
sp=SimpleNamespace(
extra_args={
"aspect_ratio": "16:9",
"use_duration_template": True,
"use_resolution_template": True,
}
),
is_t2i=False,
)
assert "The video is 2.0 seconds long" in captured[0]
assert "The video is not 2.0 seconds long" in captured[1]
assert json.loads(calls[0]["text"]) == {
"caption": "A robot",
"duration": "2s",
"fps": 24.0,
"resolution": {"H": 720, "W": 1280},
"aspect_ratio": "16:9",
}
assert "The video is 2.0 seconds long" not in calls[0]["text"]
assert calls[1]["text"] == (
"bad. The video is not 2.0 seconds long and is not of 24 FPS. This video is not of 720x1280 resolution."
)
def test_format_and_tokenize_prompts_removes_video_metadata_from_t2i_json(make_cosmos3_pipeline) -> None:
import json
pipeline = make_cosmos3_pipeline()
calls = _capture_tokenize_calls(pipeline)
pipeline._format_and_tokenize_prompts(
'{"caption": "A robot", "duration": "2s", "fps": 24}',
"",
num_frames=1,
frame_rate=24,
height=1024,
width=768,
max_sequence_length=32,
sp=SimpleNamespace(extra_args={}),
is_t2i=True,
)
assert json.loads(calls[0]["text"]) == {
"caption": "A robot",
"resolution": {"H": 1024, "W": 768},
}
@pytest.mark.parametrize(
"prompt",
[
"{malformed",
'["A robot"]',
],
)
def test_format_and_tokenize_prompts_falls_back_for_non_object_json(
make_cosmos3_pipeline,
prompt: str,
) -> None:
pipeline = make_cosmos3_pipeline()
calls = _capture_tokenize_calls(pipeline)
pipeline._format_and_tokenize_prompts(
prompt,
"",
num_frames=48,
frame_rate=24,
height=720,
width=1280,
max_sequence_length=32,
sp=SimpleNamespace(
extra_args={
"use_duration_template": True,
"use_resolution_template": True,
}
),
is_t2i=False,
)
assert calls[0]["text"] == (
f"{prompt}. The video is 2.0 seconds long and is of 24 FPS. This video is of 720x1280 resolution."
)
def test_checkpoint_key_remap() -> None:
from vllm_omni.diffusion.models.cosmos3.pipeline_cosmos3 import Cosmos3OmniDiffusersPipeline
remaps = {
"embed_tokens.weight": "transformer.language_model.embed_tokens.weight",
@@ -847,9 +1470,14 @@ def test_prompt_formatting_and_checkpoint_key_remap(make_cosmos3_pipeline) -> No
"layers.3.self_attn.to_q.weight": "transformer.language_model.layers.3.self_attn.to_q.weight",
"layers.3.self_attn.to_out.weight": "transformer.language_model.layers.3.self_attn.to_out.weight",
"layers.3.self_attn.norm_q.weight": "transformer.language_model.layers.3.self_attn.norm_q.weight",
"layers.3.self_attn.k_norm_und_for_gen.weight": (
"transformer.language_model.layers.3.self_attn.k_norm_und_for_gen.weight"
),
"layers.3.self_attn.add_q_proj.weight": "transformer.gen_layers.3.cross_attention.to_q.weight",
"layers.3.self_attn.to_add_out.weight": "transformer.gen_layers.3.cross_attention.to_out.weight",
"layers.3.self_attn.norm_added_q.weight": "transformer.gen_layers.3.cross_attention.norm_q.weight",
"layers.3.mlp_moe_gen.up_proj.weight": "transformer.gen_layers.3.mlp.up_proj.weight",
"layers.3.mlp_moe_gen.down_proj.weight": "transformer.gen_layers.3.mlp.down_proj.weight",
"transformer.model.layers.3.self_attn.add_k_proj.weight": (
"transformer.gen_layers.3.cross_attention.to_k.weight"
),
@@ -1193,9 +1821,9 @@ def test_forward_transfer_uses_source_fps_except_wsm(make_cosmos3_pipeline, hint
pipeline._prepare_transfer_latents = fake_prepare
pipeline.diffuse_transfer = fake_diffuse_transfer
def fake_set_flow_shift(target, *, use_karras_sigmas=None):
def fake_set_flow_shift(target):
captured.setdefault("flow_shifts", []).append(target)
captured.setdefault("scheduler_use_karras_sigmas", []).append(use_karras_sigmas)
pipeline._current_flow_shift = float(target)
pipeline._set_flow_shift = fake_set_flow_shift
pipeline._decode_latents = lambda latents: torch.zeros(1, 3, 5, 16, 16, device="meta")
@@ -1230,8 +1858,8 @@ def test_forward_transfer_uses_source_fps_except_wsm(make_cosmos3_pipeline, hint
assert captured["format_frame_rate"] == expected_fps
assert captured["shared_kwargs"]["fps"] == expected_fps
assert captured["flow_shifts"] == [10.0]
# Transfer uses the V2V flow-sigma schedule (karras off) so flow_shift applies.
assert captured["scheduler_use_karras_sigmas"] == [False]
# Transfer applies the V2V flow shift when building its timestep schedule.
assert [call["shift"] for call in pipeline.scheduler.set_timesteps_calls] == [10.0]
assert output.output["metadata"]["video"]["fps"] == expected_fps
assert output.output["payload"]["video"].device.type == "meta"
@@ -1391,9 +2019,9 @@ class TestForwardRouting:
pipeline._format_and_tokenize_prompts = fake_format
pipeline._prepare_latents = fake_prepare
def fake_set_flow_shift(target, *, use_karras_sigmas=None):
def fake_set_flow_shift(target):
captured.setdefault("flow_shifts", []).append(target)
captured.setdefault("scheduler_use_karras_sigmas", []).append(use_karras_sigmas)
pipeline._current_flow_shift = float(target)
pipeline._set_flow_shift = fake_set_flow_shift
pipeline.diffuse = fake_diffuse
@@ -1420,7 +2048,7 @@ class TestForwardRouting:
{
"key": "video",
"is_t2i": False,
"flow": [1.0],
"flow": [10.0],
"steps": [35],
"frames": 189,
},
@@ -1443,8 +2071,8 @@ class TestForwardRouting:
assert captured["format"]["is_t2i"] is expected["is_t2i"]
assert captured["format"]["num_frames"] == expected["frames"]
assert captured["flow_shifts"] == expected["flow"]
assert captured["scheduler_use_karras_sigmas"] == [None]
assert [call[0] for call in pipeline.scheduler.set_timesteps_calls] == expected["steps"]
assert [call["num_inference_steps"] for call in pipeline.scheduler.set_timesteps_calls] == expected["steps"]
assert all(call["shift"] == expected["flow"][0] for call in pipeline.scheduler.set_timesteps_calls)
def test_forward_i2v_sound_and_action_routes(self, make_cosmos3_pipeline) -> None:
pipeline = make_cosmos3_pipeline()
@@ -1491,7 +2119,6 @@ class TestForwardRouting:
)
)
assert captured["flow_shifts"][-1] == 10.0
assert captured["scheduler_use_karras_sigmas"][-1] is False
assert captured["format"]["negative_prompt"] == ""
assert captured["diffuse_calls"][-1]["shared_kwargs"]["noisy_frame_mask"] is v2v_mask
assert captured["diffuse_calls"][-1]["condition_latents"] is v2v_condition
@@ -15,6 +15,25 @@ from vllm_omni.platforms import current_omni_platform
pytestmark = [pytest.mark.core_model, pytest.mark.cpu, pytest.mark.diffusion]
@pytest.fixture(autouse=True)
def _single_rank_tensor_parallel(monkeypatch: pytest.MonkeyPatch) -> None:
"""Provide the TP metadata required by vLLM parallel linear layers."""
from vllm.model_executor import parameter
from vllm.model_executor.layers import linear
from vllm_omni.diffusion.models.cosmos3 import (
transformer_cosmos3,
transformer_cosmos3_edge,
)
monkeypatch.setattr(linear, "get_tensor_model_parallel_rank", lambda: 0)
monkeypatch.setattr(linear, "get_tensor_model_parallel_world_size", lambda: 1)
monkeypatch.setattr(parameter, "get_tensor_model_parallel_rank", lambda: 0)
monkeypatch.setattr(parameter, "get_tensor_model_parallel_world_size", lambda: 1)
monkeypatch.setattr(transformer_cosmos3, "get_tensor_model_parallel_world_size", lambda: 1)
monkeypatch.setattr(transformer_cosmos3_edge, "get_tensor_model_parallel_world_size", lambda: 1)
@pytest.fixture
def accelerator_device() -> torch.device:
"""Provide an accelerator device, skipping when none is available."""
@@ -40,6 +59,22 @@ def _tiny_cosmos3_config(**overrides):
return config
def _tiny_cosmos3_edge_config(**overrides):
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
COSMOS3_EDGE_BACKBONE_TYPE,
)
config = _tiny_cosmos3_config(
latent_patch_size=2,
latent_channel=48,
temporal_compression_factor=4,
backbone_type=COSMOS3_EDGE_BACKBONE_TYPE,
qk_norm_for_text=False,
)
config.update(overrides)
return config
def test_mrope_position_ids_cover_text_video_sound_and_action() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import (
compute_mrope_position_ids_action,
@@ -78,6 +113,16 @@ def test_mrope_position_ids_cover_text_video_sound_and_action() -> None:
assert action_offset == 14
def test_timestep_embedder_stores_frequencies_in_fp32() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import (
TimestepEmbedder,
)
embedder = TimestepEmbedder(hidden_size=8, frequency_embedding_size=16)
assert embedder.freqs.dtype == torch.float32
@pytest.mark.parametrize(
("key", "value"),
[
@@ -97,30 +142,245 @@ def test_validate_supported_config_rejects_unsupported_flags(key: str, value) ->
Cosmos3VFMTransformer._validate_supported_config(None)
def test_edge_config_resolves_nemotron_defaults() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
)
model = Cosmos3EdgeVFMTransformer(
SimpleNamespace(
tf_model_config=_tiny_cosmos3_edge_config(
layer_norm_epsilon=1e-5,
rope_scaling={},
rope_parameters={"mrope_section": [24, 20, 20]},
),
dtype=torch.float32,
)
)
assert model.rms_norm_eps == 1e-5
assert model.rope_theta == 100_000_000
assert model.mrope_section == [24, 20, 20]
assert model.latent_channel_size == 48
assert model.latent_patch_size == 2
assert model.temporal_compression_factor == 4
def test_edge_config_requires_backbone_type() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
)
config = _tiny_cosmos3_edge_config()
config.pop("backbone_type")
with pytest.raises(ValueError, match="must declare backbone_type"):
Cosmos3EdgeVFMTransformer(
SimpleNamespace(
tf_model_config=config,
dtype=torch.float32,
)
)
def test_edge_und_and_gen_use_relu2_and_no_und_qk_norm() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
Cosmos3Relu2MLP,
)
model = Cosmos3EdgeVFMTransformer(
SimpleNamespace(
tf_model_config=_tiny_cosmos3_edge_config(
num_hidden_layers=1,
use_k_norm_und_for_gen=False,
),
dtype=torch.float32,
)
)
layer = model.language_model.layers[0]
gen_layer = model.gen_layers[0]
assert isinstance(layer.mlp, Cosmos3Relu2MLP)
assert not hasattr(layer.mlp, "gate_proj")
assert isinstance(gen_layer.mlp, Cosmos3Relu2MLP)
assert not hasattr(gen_layer.mlp, "gate_proj")
assert not hasattr(layer.self_attn, "norm_q")
assert not hasattr(layer.self_attn, "norm_k")
assert layer.self_attn.k_norm_und_for_gen is None
def test_edge_creates_gen_k_norm_when_enabled() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import RMSNorm
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
)
model = Cosmos3EdgeVFMTransformer(
SimpleNamespace(
tf_model_config=_tiny_cosmos3_edge_config(
num_hidden_layers=1,
qk_norm_for_diffusion=True,
use_k_norm_und_for_gen=True,
),
dtype=torch.float32,
)
)
k_norm = model.language_model.layers[0].self_attn.k_norm_und_for_gen
assert model.use_k_norm_und_for_gen is True
assert isinstance(k_norm, RMSNorm)
assert isinstance(model.gen_layers[0].cross_attention.norm_q, RMSNorm)
assert isinstance(model.gen_layers[0].cross_attention.norm_k, RMSNorm)
def test_edge_supports_no_gen_qk_norm_variant() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
)
model = Cosmos3EdgeVFMTransformer(
SimpleNamespace(
tf_model_config=_tiny_cosmos3_edge_config(
num_hidden_layers=1,
qk_norm_for_diffusion=False,
use_k_norm_und_for_gen=False,
),
dtype=torch.float32,
)
)
assert model.qk_norm_for_diffusion is False
assert model.language_model.layers[0].self_attn.k_norm_und_for_gen is None
assert model.gen_layers[0].cross_attention.qk_norm is False
assert not hasattr(model.gen_layers[0].cross_attention, "norm_q")
assert not hasattr(model.gen_layers[0].cross_attention, "norm_k")
def test_edge_validates_required_relu2_weights() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeVFMTransformer,
)
model = object.__new__(Cosmos3EdgeVFMTransformer)
nn.Module.__init__(model)
model.num_hidden_layers = 1
model.qk_norm_for_diffusion = True
model.use_k_norm_und_for_gen = True
complete = {
"transformer.language_model.layers.0.mlp.up_proj.weight",
"transformer.language_model.layers.0.mlp.down_proj.weight",
"transformer.language_model.layers.0.self_attn.k_norm_und_for_gen.weight",
"transformer.gen_layers.0.mlp.up_proj.weight",
"transformer.gen_layers.0.mlp.down_proj.weight",
}
model.validate_loaded_weights(complete)
mlp_weights = complete - {
"transformer.language_model.layers.0.self_attn.k_norm_und_for_gen.weight",
}
for missing_weight in mlp_weights:
with pytest.raises(ValueError, match="missing required weights"):
model.validate_loaded_weights(complete - {missing_weight})
missing_k_norm = complete - {"transformer.language_model.layers.0.self_attn.k_norm_und_for_gen.weight"}
with pytest.raises(ValueError, match=r"self_attn\.k_norm_und_for_gen"):
model.validate_loaded_weights(missing_k_norm)
model.use_k_norm_und_for_gen = False
model.validate_loaded_weights(missing_k_norm)
def test_edge_gen_cached_k_is_normalized_but_reasoner_uses_raw_k() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import RMSNorm
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeCausalAttention,
)
class ScaleProjection(nn.Module):
def __init__(self, scale: float) -> None:
super().__init__()
self.scale = scale
def forward(self, x: torch.Tensor) -> torch.Tensor:
return x * self.scale
class IdentityOutput(nn.Module):
def forward(self, x: torch.Tensor) -> torch.Tensor:
return x
class RawKAttention(nn.Module):
def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor:
del v
return q + k
attn = object.__new__(Cosmos3EdgeCausalAttention)
nn.Module.__init__(attn)
attn.hidden_size = 4
attn.num_heads = 2
attn.num_kv_heads = 2
attn.head_dim = 2
attn.num_heads_local = 2
attn.num_kv_heads_local = 2
attn.to_q = ScaleProjection(1.0)
attn.to_k = ScaleProjection(1.0)
attn.to_v = ScaleProjection(1.0)
attn.to_out = IdentityOutput()
attn.k_norm_und_for_gen = RMSNorm(2, eps=1e-5)
attn.attn = RawKAttention()
hidden_states = torch.tensor(
[[[1.0, 2.0, 3.0, 4.0], [2.0, 1.0, 4.0, 3.0]]],
dtype=torch.float32,
)
freqs_cos = torch.ones(1, 2, 1, 2)
freqs_sin = torch.zeros(1, 2, 1, 2)
reasoner_out, gen_k, gen_v = attn(hidden_states, freqs_cos, freqs_sin)
attn.to_k.scale = 3.0
scaled_reasoner_out, scaled_gen_k, scaled_gen_v = attn(hidden_states, freqs_cos, freqs_sin)
torch.testing.assert_close(gen_k, scaled_gen_k)
torch.testing.assert_close(gen_v, scaled_gen_v)
assert not torch.allclose(reasoner_out, scaled_reasoner_out)
def test_transformer_sharding_offload_and_patch_round_trip_contracts() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import Cosmos3LanguageModel, Cosmos3VFMTransformer
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3_edge import (
Cosmos3EdgeLanguageModel,
Cosmos3EdgeVFMTransformer,
)
model = object.__new__(Cosmos3VFMTransformer)
nn.Module.__init__(model)
model.language_model = nn.Module()
model.language_model.layers = nn.ModuleList([nn.Linear(2, 2) for _ in range(2)])
model.gen_layers = nn.ModuleList([nn.Linear(2, 2)])
model.norm_moe_gen = nn.LayerNorm(2)
for transformer_cls, language_model_cls in (
(Cosmos3VFMTransformer, Cosmos3LanguageModel),
(Cosmos3EdgeVFMTransformer, Cosmos3EdgeLanguageModel),
):
model = object.__new__(transformer_cls)
nn.Module.__init__(model)
model.language_model = nn.Module()
model.language_model.layers = nn.ModuleList([nn.Linear(2, 2) for _ in range(2)])
model.gen_layers = nn.ModuleList([nn.Linear(2, 2)])
model.norm_moe_gen = nn.LayerNorm(2)
matched = [
name
for name, module in model.named_modules()
if any(condition(name, module) for condition in model._hsdp_shard_conditions)
]
assert matched == ["language_model.layers.0", "language_model.layers.1", "gen_layers.0"]
assert Cosmos3VFMTransformer._layerwise_offload_blocks_attrs == ["gen_layers"]
assert Cosmos3LanguageModel._layerwise_offload_blocks_attrs == ["layers"]
assert Cosmos3VFMTransformer._repeated_blocks == ["Cosmos3GenDecoderLayer"]
matched = [
name
for name, module in model.named_modules()
if any(condition(name, module) for condition in model._hsdp_shard_conditions)
]
assert matched == ["language_model.layers.0", "language_model.layers.1", "gen_layers.0"]
assert transformer_cls._layerwise_offload_blocks_attrs == ["gen_layers"]
assert language_model_cls._layerwise_offload_blocks_attrs == ["layers"]
assert transformer_cls._repeated_blocks == ["Cosmos3GenDecoderLayer"]
model.latent_patch_size = 2
model.latent_channel_size = 3
latents = torch.arange(1 * 3 * 1 * 3 * 5, dtype=torch.float32).reshape(1, 3, 1, 3, 5)
torch.testing.assert_close(model.unpatchify(model.patchify(latents, t=1, h=3, w=5), t=1, h=3, w=5), latents)
model.latent_patch_size = 2
model.latent_channel_size = 3
latents = torch.arange(1 * 3 * 1 * 3 * 5, dtype=torch.float32).reshape(1, 3, 1, 3, 5)
torch.testing.assert_close(
model.unpatchify(model.patchify(latents, t=1, h=3, w=5), t=1, h=3, w=5),
latents,
)
def test_forward_returns_video_prediction(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -142,6 +402,17 @@ def test_forward_returns_video_prediction(monkeypatch: pytest.MonkeyPatch) -> No
assert tuple(output.shape) == (1, 2, 1, 2, 2)
def test_qwen_gen_mlp_remains_gated() -> None:
from vllm_omni.diffusion.models.cosmos3.transformer_cosmos3 import Cosmos3GatedMLP, Cosmos3VFMTransformer
model = Cosmos3VFMTransformer(
SimpleNamespace(tf_model_config=_tiny_cosmos3_config(num_hidden_layers=1), dtype=torch.float32)
)
assert isinstance(model.gen_layers[0].mlp, Cosmos3GatedMLP)
assert hasattr(model.gen_layers[0].mlp, "gate_proj")
def test_model_cpu_offload_swaps_back_to_generator(monkeypatch: pytest.MonkeyPatch) -> None:
from vllm_omni.diffusion.models.cosmos3 import transformer_cosmos3
@@ -42,8 +42,6 @@ def _server_args() -> list[str]:
return [
"--num-gpus",
"1",
"--model-class-name",
"Cosmos3OmniDiffusersPipeline",
"--stage-init-timeout",
"900",
"--init-timeout",
+9 -7
View File
@@ -60,8 +60,9 @@ def test_sensenova_extra_registry_declares_request_and_response_params() -> None
@pytest.mark.core_model
@pytest.mark.cpu
def test_cosmos3_extra_registry_declares_request_and_response_params() -> None:
assert get_extra_body_params("Cosmos3OmniDiffusersPipeline") == frozenset(
@pytest.mark.parametrize("pipeline_name", ["Cosmos3OmniDiffusersPipeline", "Cosmos3OmniPipeline"])
def test_cosmos3_extra_registry_declares_request_and_response_params(pipeline_name: str) -> None:
assert get_extra_body_params(pipeline_name) == frozenset(
{
"flow_shift",
"max_sequence_length",
@@ -98,7 +99,7 @@ def test_cosmos3_extra_registry_declares_request_and_response_params() -> None:
"session_id",
}
)
assert get_extra_output_params("Cosmos3OmniDiffusersPipeline") == frozenset(
assert get_extra_output_params(pipeline_name) == frozenset(
{
"action",
"raw_action_dim",
@@ -106,7 +107,7 @@ def test_cosmos3_extra_registry_declares_request_and_response_params() -> None:
"action_mode",
}
)
assert should_init_extra_args_for_non_diffusion_stages("Cosmos3OmniDiffusersPipeline") is False
assert should_init_extra_args_for_non_diffusion_stages(pipeline_name) is False
@pytest.mark.core_model
@@ -224,9 +225,10 @@ def test_ming_flash_omni_image_to_image_prompt_builder() -> None:
@pytest.mark.core_model
@pytest.mark.cpu
def test_cosmos3_text_to_image_prompt_builder_selects_image_modality() -> None:
@pytest.mark.parametrize("pipeline_name", ["Cosmos3OmniDiffusersPipeline", "Cosmos3OmniPipeline"])
def test_cosmos3_text_to_image_prompt_builder_selects_image_modality(pipeline_name: str) -> None:
assert build_text_to_image_prompt(
"Cosmos3OmniDiffusersPipeline",
pipeline_name,
prompt="a red sports car at golden hour",
negative_prompt="blurry, distorted",
height=1024,
@@ -237,7 +239,7 @@ def test_cosmos3_text_to_image_prompt_builder_selects_image_modality() -> None:
"negative_prompt": "blurry, distorted",
}
assert build_text_to_image_prompt(
"Cosmos3OmniDiffusersPipeline",
pipeline_name,
prompt="a red sports car",
negative_prompt=None,
) == {"prompt": "a red sports car", "modalities": ["image"]}
@@ -71,6 +71,7 @@ EXCLUDED_MODELS = [
"OmniVoicePipeline",
"OmniVoice",
"Cosmos3OmniDiffusersPipeline",
"Cosmos3OmniPipeline",
"DiffusersAdapterPipeline",
"HiDreamImagePipeline",
"DreamZeroPipeline",
+1
View File
@@ -1016,6 +1016,7 @@ CUSTOM_DIT_ENABLERS.update(
"Wan22VACEPipeline": enable_cache_for_wan22,
"Wan22S2VPipeline": enable_cache_for_wan22_s2v,
"Cosmos3OmniDiffusersPipeline": enable_cache_for_cosmos3,
"Cosmos3OmniPipeline": enable_cache_for_cosmos3,
"Krea2Pipeline": enable_cache_for_krea2,
}
)
@@ -7,10 +7,12 @@ from .pipeline_cosmos3 import (
get_cosmos3_pre_process_func,
)
from .transformer_cosmos3 import Cosmos3VFMTransformer
from .transformer_cosmos3_edge import Cosmos3EdgeVFMTransformer
__all__ = [
"Cosmos3OmniDiffusersPipeline",
"get_cosmos3_post_process_func",
"get_cosmos3_pre_process_func",
"Cosmos3VFMTransformer",
"Cosmos3EdgeVFMTransformer",
]
@@ -29,6 +29,7 @@ is produced from sound latents rather than from ``multi_modal_data["audio"]``.
from __future__ import annotations
import json
import math
import os
import time
@@ -39,7 +40,7 @@ from typing import Any, ClassVar
import numpy as np
import PIL.Image
import torch
from diffusers import UniPCMultistepScheduler
from diffusers import FlowMatchEulerDiscreteScheduler
from diffusers.utils.torch_utils import randn_tensor
from diffusers.video_processor import VideoProcessor
from torch import nn
@@ -61,6 +62,9 @@ from vllm_omni.diffusion.models.interface import (
SupportsComponentDiscovery,
)
from vllm_omni.diffusion.models.progress_bar import ProgressBarMixin, _is_rank_zero
from vllm_omni.diffusion.models.schedulers.scheduling_flow_unipc_multistep import (
FlowUniPCMultistepScheduler,
)
from vllm_omni.diffusion.profiler.diffusion_pipeline_profiler import DiffusionPipelineProfilerMixin
from vllm_omni.diffusion.request import OmniDiffusionRequest
from vllm_omni.diffusion.worker.request_batch import DiffusionRequestBatch
@@ -94,7 +98,8 @@ from .transfer import (
transfer_max_frames_from_extra_args,
uint8_cthw_to_normalized_5d,
)
from .transformer_cosmos3 import Cosmos3VFMTransformer, resolve_sound_gen
from .transformer_cosmos3 import Cosmos3VFMTransformer, _tf_config_get, resolve_sound_gen
from .transformer_cosmos3_edge import COSMOS3_EDGE_BACKBONE_TYPE, Cosmos3EdgeVFMTransformer
from .utils import (
COSMOS3_DEFAULT_CONDITION_FRAME_INDEXES_VISION,
COSMOS3_VAE_TEMPORAL_COMPRESSION,
@@ -154,6 +159,7 @@ COSMOS3_T2V_DEFAULT_WIDTH = 1280
COSMOS3_T2V_DEFAULT_NUM_FRAMES = 189
COSMOS3_T2V_DEFAULT_NUM_INFERENCE_STEPS = 35
COSMOS3_T2V_DEFAULT_GUIDANCE_SCALE = 6.0
COSMOS3_VIDEO_DEFAULT_FLOW_SHIFT = 10.0
COSMOS3_T2I_DEFAULT_HEIGHT = 1024
COSMOS3_T2I_DEFAULT_WIDTH = 1024
@@ -162,12 +168,73 @@ COSMOS3_T2I_DEFAULT_GUIDANCE_SCALE = 7.0
COSMOS3_T2I_DEFAULT_FLOW_SHIFT = 3.0
COSMOS3_T2I_DEFAULT_GUIDANCE_INTERVAL: tuple[float, float] = (400.0, 1000.0)
COSMOS3_EDGE_T2V_DEFAULT_HEIGHT = 480
COSMOS3_EDGE_T2V_DEFAULT_WIDTH = 832
COSMOS3_EDGE_T2V_DEFAULT_GUIDANCE_SCALE = 5.0
COSMOS3_EDGE_VIDEO_DEFAULT_FLOW_SHIFT = 3.0
COSMOS3_EDGE_T2I_DEFAULT_HEIGHT = 640
COSMOS3_EDGE_T2I_DEFAULT_WIDTH = 640
COSMOS3_DISTILLED_CHECKPOINT_SCHEDULER_CLASS = "FlowMatchEulerDiscreteScheduler"
# Truncation cap on the prompt token count (shared by T2I and T2V). Prompts
# are tokenized to their natural length (no padding); this only bounds the
# UND pathway / GEN cross-attention cost for pathologically long prompts.
COSMOS3_DEFAULT_MAX_SEQUENCE_LENGTH = 4096
def _ceil_video_num_frames(num_frames: int, temporal_compression_factor: int) -> int:
"""Round a video length up to the causal VAE's ``factor * k + 1`` grid."""
if temporal_compression_factor <= 0:
raise ValueError(f"Cosmos3 temporal_compression_factor must be positive, got {temporal_compression_factor}.")
if num_frames <= 1:
return num_frames
latent_frames = math.ceil((num_frames - 1) / temporal_compression_factor)
return latent_frames * temporal_compression_factor + 1
def _format_json_object_prompt(
prompt: str,
*,
num_frames: int,
frame_rate: float,
height: int,
width: int,
aspect_ratio: str | None,
) -> str | None:
"""Match Imaginaire4 metadata injection for JSON-object prompts."""
try:
prompt_obj = json.loads(prompt)
except (json.JSONDecodeError, TypeError, ValueError):
return None
if not isinstance(prompt_obj, dict):
return None
metadata: dict[str, Any] = {}
if num_frames > 1:
duration_seconds = int(num_frames / frame_rate) if frame_rate > 0 else 0
metadata.update({"duration": f"{duration_seconds}s", "fps": float(frame_rate)})
else:
prompt_obj.pop("duration", None)
prompt_obj.pop("fps", None)
metadata["resolution"] = {"H": int(height), "W": int(width)}
if aspect_ratio is not None:
metadata["aspect_ratio"] = aspect_ratio
prompt_obj.update(metadata)
return json.dumps(prompt_obj)
def resolve_cosmos3_transformer_cls(model_config: Any) -> type[Cosmos3VFMTransformer]:
"""Select the Cosmos3 transformer implementation from transformer/config.json."""
backbone_type = _tf_config_get(model_config, "backbone_type", None)
if backbone_type is None:
return Cosmos3VFMTransformer
if backbone_type == COSMOS3_EDGE_BACKBONE_TYPE:
return Cosmos3EdgeVFMTransformer
raise ValueError(f"Unsupported Cosmos3 transformer backbone_type={backbone_type!r}.")
# ---------------------------------------------------------------------------
# Post-process function (registered in registry.py)
# ---------------------------------------------------------------------------
@@ -190,6 +257,9 @@ def get_cosmos3_pre_process_func(od_config: OmniDiffusionConfig):
from .guardrails import check_text_safety, ensure_initialized, is_guardrails_enabled
video_processor = VideoProcessor(vae_scale_factor=16)
is_edge_model = (
_tf_config_get(getattr(od_config, "tf_model_config", None), "backbone_type", None) == COSMOS3_EDGE_BACKBONE_TYPE
)
# Eager-load guardrail models at pipeline build time when the server-level
# gate is on. Per-request overrides only decide whether the loaded models
# are *invoked* — they cannot turn checks on without a server-side preload.
@@ -410,12 +480,17 @@ def get_cosmos3_pre_process_func(od_config: OmniDiffusionConfig):
extra = _extra_args(request)
transfer_requested = action_mode is None and has_transfer_hints(extra)
# Auto-calculate H/W from aspect ratio (720p max area)
# Resolve missing H/W.
if transfer_requested:
_set_transfer_size_from_image(request, image)
elif request.sampling_params.height is None or request.sampling_params.width is None:
if action_mode is not None:
_set_action_size_from_image(request, image)
elif is_edge_model:
if request.sampling_params.height is None:
request.sampling_params.height = COSMOS3_EDGE_T2V_DEFAULT_HEIGHT
if request.sampling_params.width is None:
request.sampling_params.width = COSMOS3_EDGE_T2V_DEFAULT_WIDTH
else:
max_area = 720 * 1280
aspect_ratio = image.height / image.width
@@ -823,27 +898,52 @@ class Cosmos3OmniDiffusersPipeline(
sound_latent_fps = self._sound_tokenizer.latent_fps
# --- Transformer (weights loaded later via weights_sources) ---
self.transformer = Cosmos3VFMTransformer(
transformer_cls = resolve_cosmos3_transformer_cls(od_config.tf_model_config)
self.transformer = transformer_cls(
od_config=od_config,
temporal_compression_factor=self.vae_scale_factor_temporal,
sound_gen=sound_gen,
sound_dim=sound_dim,
sound_latent_fps=sound_latent_fps,
)
self.is_edge_model = transformer_cls is Cosmos3EdgeVFMTransformer
# --- Scheduler ---
# Load from checkpoint to preserve solver_order, timestep_spacing,
# beta_schedule, sigma bounds, flow_shift, etc. Only override
# flow_shift when explicitly requested by the user.
self.scheduler = UniPCMultistepScheduler.from_pretrained(
# Distilled model differs from regular one only by scheduler,
# distilled one uses FlowMatchEulerDiscreteScheduler, while
# regular should use FlowUniPCMultistepScheduler
scheduler_config = FlowUniPCMultistepScheduler.load_config(
model_path,
subfolder="scheduler",
local_files_only=local_files_only,
)
if od_config.flow_shift is not None:
self.scheduler = UniPCMultistepScheduler.from_config(
self.scheduler.config,
flow_shift=od_config.flow_shift,
scheduler_class_name = scheduler_config.get("_class_name")
self.is_distilled_model = False
if scheduler_class_name == COSMOS3_DISTILLED_CHECKPOINT_SCHEDULER_CLASS:
fixed_step_config = scheduler_config.get("fixed_step_sampler_config")
if not isinstance(fixed_step_config, dict) or fixed_step_config.get("sample_type") != "sde":
raise ValueError("Cosmos3 distilled scheduler requires fixed_step_sampler_config.sample_type=sde.")
t_list = fixed_step_config.get("t_list")
if not isinstance(t_list, list) or not t_list:
raise ValueError("Cosmos3 distilled scheduler requires a non-empty fixed_step_sampler_config.t_list.")
self.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
scheduler_config,
stochastic_sampling=True,
)
self._scheduler_init_t_list = list(t_list)
self.is_distilled_model = True
else:
# Preserve compatible solver settings from the checkpoint, but keep
# the base shift neutral. The concrete request shift is applied when
# FlowUniPC builds its timesteps.
self.scheduler = FlowUniPCMultistepScheduler.from_config(
scheduler_config,
shift=1.0,
use_dynamic_shifting=False,
prediction_type="flow_prediction",
)
self._cpu_scheduler_state()
@@ -862,14 +962,16 @@ class Cosmos3OmniDiffusersPipeline(
),
]
# Snapshot the loaded scheduler config so we can rebuild the
# scheduler at request time when a per-request flow_shift override
# is supplied (T2I uses shift=3.0; T2V/I2V use the engine default).
self._base_scheduler_config = self.scheduler.config
self._engine_init_flow_shift = float(getattr(self.scheduler.config, "flow_shift", 1.0) or 1.0)
# An engine-level override becomes the video default; otherwise use
# the matching regular/Edge default. Per-request values still take
# precedence.
default_video_flow_shift = (
COSMOS3_EDGE_VIDEO_DEFAULT_FLOW_SHIFT if self.is_edge_model else COSMOS3_VIDEO_DEFAULT_FLOW_SHIFT
)
self._engine_init_flow_shift = float(
od_config.flow_shift if od_config.flow_shift is not None else default_video_flow_shift
)
self._current_flow_shift = self._engine_init_flow_shift
self._base_scheduler_use_karras_sigmas = self._scheduler_use_karras_sigmas(self.scheduler.config)
self._current_scheduler_use_karras_sigmas = self._base_scheduler_use_karras_sigmas
self._guidance_scale = None
self._num_timesteps = None
@@ -989,6 +1091,7 @@ class Cosmos3OmniDiffusersPipeline(
"self_attn.to_out.": f"{und_lp}.self_attn.to_out.",
"self_attn.norm_q.": f"{und_lp}.self_attn.norm_q.",
"self_attn.norm_k.": f"{und_lp}.self_attn.norm_k.",
"self_attn.k_norm_und_for_gen.": f"{und_lp}.self_attn.k_norm_und_for_gen.",
# GEN attention
"self_attn.add_q_proj.": f"{gen_lp}.cross_attention.to_q.",
"self_attn.add_k_proj.": f"{gen_lp}.cross_attention.to_k.",
@@ -1054,6 +1157,7 @@ class Cosmos3OmniDiffusersPipeline(
loaded = loader.load_weights(_remapped_weights())
self.transformer.post_load_weights()
self.transformer.eval()
self.transformer.validate_loaded_weights(loaded)
if getattr(self.transformer, "sound_gen", False):
sound_markers = ("audio_proj_in.", "audio_proj_out.", "audio_modality_embed")
missing = [marker.rstrip(".") for marker in sound_markers if not any(marker in name for name in loaded)]
@@ -1359,6 +1463,8 @@ class Cosmos3OmniDiffusersPipeline(
inputs: RoboLabPolicyInputs,
pipeline_start: float,
) -> DiffusionOutput:
if self.is_distilled_model:
raise self._distilled_unsupported_error("RoboLab/action policy requests are unsupported.")
if not getattr(self.transformer, "action_gen", False):
raise ValueError(
"Cosmos3 RoboLab policy serving was requested, but the transformer "
@@ -1469,6 +1575,7 @@ class Cosmos3OmniDiffusersPipeline(
guidance_interval=None,
raw_action_dim=raw_action_dim,
scheduler=scheduler,
generator=generator,
)
if _is_rank_zero():
@@ -1560,38 +1667,34 @@ class Cosmos3OmniDiffusersPipeline(
raise ValueError(f"Incorrect modality value in {modalities}, expected one of {accepted_modalities}.")
return "image" in modalities
def _set_timesteps(self, num_inference_steps: int, device: str | torch.device, shift: float) -> None:
if self.is_distilled_model:
self.scheduler.set_timesteps(sigmas=self._scheduler_init_t_list, device=device)
else:
self.scheduler.set_timesteps(
num_inference_steps,
device=device,
shift=shift,
)
def _set_flow_shift(self, target_shift: float) -> None:
"""Select the shift applied by FlowUniPC when timesteps are built."""
self._current_flow_shift = float(target_shift)
@staticmethod
def _scheduler_use_karras_sigmas(config: Any) -> bool | None:
value = getattr(config, "use_karras_sigmas", None)
return None if value is None else bool(value)
def _resolve_seed(sp: OmniDiffusionSamplingParams, generator: torch.Generator | None, default: int = 42) -> int:
if sp.seed is not None:
return int(sp.seed)
if isinstance(generator, torch.Generator):
return int(generator.initial_seed())
return int(default)
def _set_flow_shift(self, target_shift: float, *, use_karras_sigmas: bool | None = None) -> None:
"""Set UniPC scheduler mode for a concrete request.
The scheduler is rebuilt from the saved base config if
the target differs from the current shift or Karras-sigma mode.
Tracking explicit scheduler state is required because the previous
mode may have rebuilt the scheduler - we cannot rely on
``self.scheduler.config`` reflecting the last requested target if a
rebuild was skipped via the equality check.
"""
target = float(target_shift)
target_use_karras_sigmas = (
self._base_scheduler_use_karras_sigmas if use_karras_sigmas is None else bool(use_karras_sigmas)
)
if (
target == float(self._current_flow_shift)
and target_use_karras_sigmas == self._current_scheduler_use_karras_sigmas
):
return
scheduler_kwargs: dict[str, Any] = {"flow_shift": target}
if use_karras_sigmas is not None:
scheduler_kwargs["use_karras_sigmas"] = bool(use_karras_sigmas)
self.scheduler = UniPCMultistepScheduler.from_config(self._base_scheduler_config, **scheduler_kwargs)
self._cpu_scheduler_state()
self._current_flow_shift = target
self._current_scheduler_use_karras_sigmas = self._scheduler_use_karras_sigmas(self.scheduler.config)
def _resolve_guidance_scale(self, sp: OmniDiffusionSamplingParams, default: float) -> float:
if self.is_distilled_model:
return 1.0
if sp.guidance_scale_provided:
return float(sp.guidance_scale)
return float(default)
def _cpu_scheduler_state(self) -> None:
# We need to move scheduler tensors to CPU, as unipc from diffusers assumes they are on CPU.
@@ -1612,6 +1715,53 @@ class Cosmos3OmniDiffusersPipeline(
def num_timesteps(self):
return self._num_timesteps
@staticmethod
def _distilled_unsupported_error(detail: str) -> ValueError:
return ValueError(
f"Cosmos3 distilled checkpoints support only text-to-image and image-to-video generation; {detail}"
)
def _validate_distilled_generation_mode(
self,
*,
is_t2i: bool,
image_tensor: torch.Tensor | None,
action_enabled: bool,
transfer_config: Cosmos3TransferConfig | None,
is_v2v: bool,
sound_enabled: bool,
) -> None:
if not self.is_distilled_model:
return
if action_enabled:
raise self._distilled_unsupported_error("action requests are unsupported.")
if transfer_config is not None:
raise self._distilled_unsupported_error("transfer requests are unsupported.")
if is_v2v:
raise self._distilled_unsupported_error("video-to-video requests are unsupported.")
if sound_enabled:
raise self._distilled_unsupported_error("sound generation is unsupported.")
if not is_t2i and image_tensor is None:
raise self._distilled_unsupported_error("text-to-video requests are unsupported.")
if is_t2i and image_tensor is not None:
raise self._distilled_unsupported_error("image-conditioned image generation is unsupported.")
def _validate_edge_generation_mode(
self,
*,
transfer_config: Cosmos3TransferConfig | None,
is_v2v: bool,
sound_enabled: bool,
) -> None:
if not self.is_edge_model:
return
if sound_enabled:
raise ValueError("Cosmos3 Edge checkpoints do not support sound generation.")
if transfer_config is not None:
raise ValueError("Cosmos3 Edge checkpoints do not support transfer inference.")
if is_v2v:
raise ValueError("Cosmos3 Edge checkpoints do not support video-to-video generation.")
# -- Prompt formatting -----------------------------------------------------
@staticmethod
@@ -1628,6 +1778,10 @@ class Cosmos3OmniDiffusersPipeline(
"""
Append duration and resolution metadata to a prompt.
"""
prompt = prompt.strip()
if duration_template is None and resolution_template is None:
return prompt
parts: list[str] = []
head = prompt.rstrip(".").strip()
if head:
@@ -1903,15 +2057,26 @@ class Cosmos3OmniDiffusersPipeline(
res_tmpl = COSMOS3_IMAGE_RESOLUTION_TEMPLATE if is_t2i else COSMOS3_RESOLUTION_TEMPLATE
else:
res_tmpl = None
prompt = self._apply_metadata_templates(
json_prompt = _format_json_object_prompt(
prompt,
num_frames,
frame_rate,
height,
width,
duration_template=dur_tmpl,
resolution_template=res_tmpl,
num_frames=num_frames,
frame_rate=frame_rate,
height=height,
width=width,
aspect_ratio=self._get_sp_param(sp, "aspect_ratio", None),
)
if json_prompt is not None:
prompt = json_prompt
else:
prompt = self._apply_metadata_templates(
prompt,
num_frames,
frame_rate,
height,
width,
duration_template=dur_tmpl,
resolution_template=res_tmpl,
)
if _is_rank_zero():
logger.info("Final prompt: '%s'", prompt)
@@ -2272,6 +2437,7 @@ class Cosmos3OmniDiffusersPipeline(
guidance_interval: tuple[float, float] | None = None,
raw_action_dim: int | None = None,
scheduler: Any | None = None,
generator: torch.Generator | None = None,
) -> torch.Tensor | tuple[torch.Tensor, ...]:
"""Denoising loop with 3-mode CFG support (parallel, sequential, none).
@@ -2383,11 +2549,23 @@ class Cosmos3OmniDiffusersPipeline(
if raw_action_dim is not None and 0 < raw_action_dim < action_pred.shape[-1]:
action_pred[..., raw_action_dim:] = 0
if action_latents is None and sound_latents is None:
latents = step_scheduler.step(video_pred, t, latents, return_dict=False)[0]
latents = step_scheduler.step(
video_pred,
t,
latents,
generator=generator,
return_dict=False,
)[0]
else:
packed_noise, shapes, numels = _pack_joint(video_pred, action_pred, sound_pred)
packed_latents, _, _ = _pack_joint(latents, action_latents, sound_latents)
packed_next = step_scheduler.step(packed_noise, t, packed_latents, return_dict=False)[0]
packed_next = step_scheduler.step(
packed_noise,
t,
packed_latents,
generator=generator,
return_dict=False,
)[0]
unpacked = _unpack_joint(packed_next, shapes, numels)
latents = unpacked[0]
idx = 1
@@ -2613,6 +2791,7 @@ class Cosmos3OmniDiffusersPipeline(
velocity_mask: torch.Tensor,
condition_latents: torch.Tensor,
guidance_interval: tuple[float, float] | None = None,
generator: torch.Generator | None = None,
) -> torch.Tensor:
def _active_at(t: torch.Tensor, interval: tuple[float, float] | None) -> bool:
if interval is None:
@@ -2724,7 +2903,13 @@ class Cosmos3OmniDiffusersPipeline(
if isinstance(noise_pred, tuple):
raise ValueError("Cosmos3 transfer diffusion expects video-only tensor predictions.")
noise_pred = noise_pred * velocity_mask
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
latents = self.scheduler.step(
noise_pred,
t,
latents,
generator=generator,
return_dict=False,
)[0]
latents = velocity_mask * latents + (1.0 - velocity_mask) * condition_latents
finally:
self._cosmos3_branch_caches = None
@@ -2741,6 +2926,8 @@ class Cosmos3OmniDiffusersPipeline(
transfer_video_tensor: torch.Tensor | None,
transfer_input_fps: float | None,
) -> DiffusionOutput:
if self.is_distilled_model:
raise self._distilled_unsupported_error("transfer requests are unsupported.")
input_frames = None
if transfer_video_tensor is not None:
input_frames = normalized_video_to_uint8_cthw(transfer_video_tensor)
@@ -2806,7 +2993,7 @@ class Cosmos3OmniDiffusersPipeline(
guidance_scale = (
float(transfer_config.guidance_scale)
if transfer_config.guidance_scale is not None
else float(sp.guidance_scale or COSMOS3_T2V_DEFAULT_GUIDANCE_SCALE)
else self._resolve_guidance_scale(sp, COSMOS3_T2V_DEFAULT_GUIDANCE_SCALE)
)
flow_shift_target = float(
transfer_config.flow_shift
@@ -2821,11 +3008,11 @@ class Cosmos3OmniDiffusersPipeline(
self._guidance_scale = guidance_scale
self._num_timesteps = num_inference_steps
self._set_flow_shift(flow_shift_target, use_karras_sigmas=False)
self._set_flow_shift(flow_shift_target)
generator = sp.generator
seed = self._resolve_seed(sp, generator)
if generator is None:
seed = sp.seed if sp.seed is not None else 42
generator = torch.Generator(device=self.device).manual_seed(seed)
cond_ids, cond_mask, uncond_ids, uncond_mask = self._format_and_tokenize_prompts(
@@ -2915,7 +3102,11 @@ class Cosmos3OmniDiffusersPipeline(
transfer_share_vision_temporal_positions=transfer_config.share_vision_temporal_positions,
)
self.scheduler.set_timesteps(num_inference_steps, device=self.device)
self._set_timesteps(
num_inference_steps,
device=self.device,
shift=self._current_flow_shift,
)
latents = self.diffuse_transfer(
latents=latents,
timesteps=self.scheduler.timesteps,
@@ -2930,6 +3121,7 @@ class Cosmos3OmniDiffusersPipeline(
shared_kwargs=shared_kwargs,
velocity_mask=velocity_mask,
condition_latents=condition_latents,
generator=generator,
)
output_video = self._decode_latents(latents).clamp(-1, 1)
previous_output = output_video
@@ -3009,6 +3201,20 @@ class Cosmos3OmniDiffusersPipeline(
action_enabled = action_mode is not None
transfer_config = resolve_transfer_config(sp, prompt_data)
action_video_tensor = video_tensor if action_enabled else None
is_v2v = video_tensor is not None and not is_t2i and not action_enabled
self._validate_distilled_generation_mode(
is_t2i=is_t2i,
image_tensor=image_tensor,
action_enabled=action_enabled,
transfer_config=transfer_config,
is_v2v=is_v2v,
sound_enabled=sound_enabled,
)
self._validate_edge_generation_mode(
transfer_config=transfer_config,
is_v2v=is_v2v,
sound_enabled=sound_enabled,
)
if transfer_config is not None:
if is_t2i:
raise ValueError("Cosmos3 transfer inference is supported only for video outputs.")
@@ -3056,31 +3262,36 @@ class Cosmos3OmniDiffusersPipeline(
raise ValueError("Cosmos3 non-action generation accepts either image or video input, not both.")
if video_tensor is not None and is_t2i:
raise ValueError("Cosmos3 video-to-video generation is supported only for video outputs.")
is_v2v = video_tensor is not None and not is_t2i and not action_enabled
# T2I and T2V share the same model + forward path; only defaults
# differ:
# T2I: 1024x1024, 50 steps, shift=3.0, guidance_interval=[400, 1000]
# T2V: 720x1280, 35 steps, shift=engine-init, no interval
# T2I and T2V share the same model and forward path; their defaults are:
# T2I: regular 1024x1024, Edge 640x640; 50 steps, shift=3.0,
# guidance_interval=[400, 1000]
# T2V: 189 frames and 35 steps; regular guidance=6, shift=10;
# Edge guidance=5, shift=3;
# no guidance interval
if is_t2i:
height = sp.height or COSMOS3_T2I_DEFAULT_HEIGHT
width = sp.width or COSMOS3_T2I_DEFAULT_WIDTH
height = sp.height or (
COSMOS3_EDGE_T2I_DEFAULT_HEIGHT if self.is_edge_model else COSMOS3_T2I_DEFAULT_HEIGHT
)
width = sp.width or (COSMOS3_EDGE_T2I_DEFAULT_WIDTH if self.is_edge_model else COSMOS3_T2I_DEFAULT_WIDTH)
num_frames = 1
num_inference_steps = sp.num_inference_steps or COSMOS3_T2I_DEFAULT_NUM_INFERENCE_STEPS
guidance_scale = sp.guidance_scale if sp.guidance_scale else COSMOS3_T2I_DEFAULT_GUIDANCE_SCALE
guidance_scale = self._resolve_guidance_scale(sp, COSMOS3_T2I_DEFAULT_GUIDANCE_SCALE)
default_flow_shift = COSMOS3_T2I_DEFAULT_FLOW_SHIFT
default_guidance_interval: tuple[float, float] | None = COSMOS3_T2I_DEFAULT_GUIDANCE_INTERVAL
batch_size = max(1, int(sp.num_outputs_per_prompt or 1))
else:
height = sp.height or COSMOS3_T2V_DEFAULT_HEIGHT
width = sp.width or COSMOS3_T2V_DEFAULT_WIDTH
height = sp.height or (
COSMOS3_EDGE_T2V_DEFAULT_HEIGHT if self.is_edge_model else COSMOS3_T2V_DEFAULT_HEIGHT
)
width = sp.width or (COSMOS3_EDGE_T2V_DEFAULT_WIDTH if self.is_edge_model else COSMOS3_T2V_DEFAULT_WIDTH)
default_guidance_scale = (
COSMOS3_EDGE_T2V_DEFAULT_GUIDANCE_SCALE if self.is_edge_model else COSMOS3_T2V_DEFAULT_GUIDANCE_SCALE
)
num_frames = sp.num_frames or COSMOS3_T2V_DEFAULT_NUM_FRAMES
num_inference_steps = sp.num_inference_steps or COSMOS3_T2V_DEFAULT_NUM_INFERENCE_STEPS
guidance_scale = sp.guidance_scale if sp.guidance_scale else COSMOS3_T2V_DEFAULT_GUIDANCE_SCALE
# Fall back to the engine-init shift, NOT None: passing None
# to ``_set_flow_shift`` would leak a prior T2I rebuild
# (shift=3.0) into a subsequent video request.
default_flow_shift = COSMOS3_V2V_DEFAULT_FLOW_SHIFT if is_v2v else self._engine_init_flow_shift
guidance_scale = self._resolve_guidance_scale(sp, default_guidance_scale)
default_flow_shift = self._engine_init_flow_shift
default_guidance_interval = None
batch_size = 1 # Existing video pipeline assumes B=1.
@@ -3103,9 +3314,23 @@ class Cosmos3OmniDiffusersPipeline(
f"or action_chunk_size + 1; got num_frames={num_frames}, action_chunk_size={action_chunk_size}."
)
num_inference_steps = sp.num_inference_steps or 30
guidance_scale = sp.guidance_scale if sp.guidance_scale is not None else 1.0
guidance_scale = self._resolve_guidance_scale(sp, 1.0)
default_flow_shift = 5.0
if not is_t2i and not action_enabled:
requested_num_frames = int(num_frames)
num_frames = _ceil_video_num_frames(
requested_num_frames,
self.vae_scale_factor_temporal,
)
if num_frames != requested_num_frames and _is_rank_zero():
logger.info(
"Rounded Cosmos3 num_frames from %d to %d for temporal compression factor %d.",
requested_num_frames,
num_frames,
self.vae_scale_factor_temporal,
)
domain_id = None
if action_enabled:
domain_id = resolve_domain_id(
@@ -3146,13 +3371,12 @@ class Cosmos3OmniDiffusersPipeline(
self._guidance_scale = guidance_scale
self._num_timesteps = num_inference_steps
# Always resolve to a concrete target shift for this request, then
# update the shared Diffusers scheduler.
self._set_flow_shift(flow_shift_target, use_karras_sigmas=False if is_v2v else None)
# Always resolve to a concrete target shift for this request.
self._set_flow_shift(flow_shift_target)
generator = sp.generator
seed = self._resolve_seed(sp, generator)
if generator is None:
seed = sp.seed if sp.seed is not None else 42
generator = torch.Generator(device=self.device).manual_seed(seed)
# --- Format prompts & tokenize (B=1; reused across loop iterations
@@ -3293,7 +3517,11 @@ class Cosmos3OmniDiffusersPipeline(
)
def _run_diffusion(start_latents):
self.scheduler.set_timesteps(num_inference_steps, device=self.device)
self._set_timesteps(
num_inference_steps,
device=self.device,
shift=self._current_flow_shift,
)
scheduler = self.scheduler
return self.diffuse(
latents=start_latents,
@@ -3314,6 +3542,7 @@ class Cosmos3OmniDiffusersPipeline(
guidance_interval=guidance_interval,
raw_action_dim=raw_action_dim,
scheduler=scheduler,
generator=generator,
)
if is_t2i and batch_size > 1:
@@ -396,7 +396,6 @@ class TimestepEmbedder(nn.Module):
hidden_size: int,
frequency_embedding_size: int = 256,
max_period: int = 10000,
target_dtype: torch.dtype = torch.bfloat16,
) -> None:
super().__init__()
# Following diffusers naming pattern here for checkpoint compatibility.
@@ -407,7 +406,7 @@ class TimestepEmbedder(nn.Module):
self.hidden_size = hidden_size
half = frequency_embedding_size // 2
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=target_dtype) / half)
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half)
self.register_buffer("freqs", freqs, persistent=False)
def forward(self, t: torch.Tensor) -> torch.Tensor:
@@ -587,6 +586,7 @@ class Cosmos3CrossAttention(nn.Module):
head_dim: int,
rms_norm_eps: float,
quant_config: QuantizationConfig | None = None,
qk_norm: bool = True,
prefix: str = "",
) -> None:
super().__init__()
@@ -636,8 +636,10 @@ class Cosmos3CrossAttention(nn.Module):
prefix=f"{prefix}.to_out",
)
self.norm_q = RMSNorm(self.head_dim, eps=rms_norm_eps)
self.norm_k = RMSNorm(self.head_dim, eps=rms_norm_eps)
self.qk_norm = qk_norm
if self.qk_norm:
self.norm_q = RMSNorm(self.head_dim, eps=rms_norm_eps)
self.norm_k = RMSNorm(self.head_dim, eps=rms_norm_eps)
self.attn = FrameworkAttention(
num_heads=self.num_heads,
@@ -721,8 +723,9 @@ class Cosmos3CrossAttention(nn.Module):
v = self.to_v(hidden_states).view(B, S_gen, self.num_kv_heads_local, self.head_dim)
# Per-head QK norm
q = F.rms_norm(q, (self.head_dim,), self.norm_q.weight, eps=self.norm_q.variance_epsilon)
k = F.rms_norm(k, (self.head_dim,), self.norm_k.weight, eps=self.norm_k.variance_epsilon)
if self.qk_norm:
q = F.rms_norm(q, (self.head_dim,), self.norm_q.weight, eps=self.norm_q.variance_epsilon)
k = F.rms_norm(k, (self.head_dim,), self.norm_k.weight, eps=self.norm_k.variance_epsilon)
# Qwen3-style RoPE
q, k = _apply_rotary_pos_emb(q, k, freqs_cos, freqs_sin)
@@ -806,6 +809,8 @@ class Cosmos3GenDecoderLayer(nn.Module):
head_dim: int,
rms_norm_eps: float,
quant_config: QuantizationConfig | None = None,
mlp_cls: type[nn.Module] = Cosmos3GatedMLP,
qk_norm: bool = True,
prefix: str = "",
) -> None:
super().__init__()
@@ -817,11 +822,12 @@ class Cosmos3GenDecoderLayer(nn.Module):
head_dim=head_dim,
rms_norm_eps=rms_norm_eps,
quant_config=quant_config,
qk_norm=qk_norm,
prefix=f"{prefix}.cross_attention",
)
self.input_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps)
self.post_attention_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps)
self.mlp = Cosmos3GatedMLP(
self.mlp = mlp_cls(
hidden_size=hidden_size,
intermediate_size=intermediate_size,
quant_config=quant_config,
@@ -996,6 +1002,9 @@ class Cosmos3VFMTransformer(nn.Module):
packed_modules_mapping = {}
_language_model_cls = Cosmos3LanguageModel
_gen_mlp_cls = Cosmos3GatedMLP
@staticmethod
def _is_transformer_block(name: str, module) -> bool:
return ("gen_layers" in name or "language_model.layers" in name) and name.split(".")[-1].isdigit()
@@ -1032,6 +1041,25 @@ class Cosmos3VFMTransformer(nn.Module):
if actual != expected:
raise ValueError(f"Unsupported Cosmos3 transformer config: {key}={actual!r}; expected {expected!r}.")
@classmethod
def _resolve_rms_norm_eps(cls, model_config: Any) -> float:
return float(_tf_config_get(model_config, "rms_norm_eps", 1e-6))
@classmethod
def _resolve_rope_theta(cls, model_config: Any) -> float:
return float(_tf_config_get(model_config, "rope_theta", 5_000_000))
@classmethod
def _resolve_mrope_section(cls, model_config: Any) -> list[int]:
rope_scaling = _tf_config_get(model_config, "rope_scaling", {}) or {}
return list(rope_scaling.get("mrope_section", [24, 20, 20]))
def _language_model_kwargs(self) -> dict[str, Any]:
return {}
def validate_loaded_weights(self, loaded: set[str]) -> None:
del loaded
def __init__(
self,
od_config: OmniDiffusionConfig,
@@ -1044,7 +1072,6 @@ class Cosmos3VFMTransformer(nn.Module):
super().__init__()
model_config = od_config.tf_model_config
self._validate_supported_config(model_config)
rope_scaling = _tf_config_get(model_config, "rope_scaling", {}) or {}
self.hidden_size = int(_tf_config_get(model_config, "hidden_size", 4096))
self.num_hidden_layers = int(_tf_config_get(model_config, "num_hidden_layers", 36))
@@ -1053,9 +1080,10 @@ class Cosmos3VFMTransformer(nn.Module):
self.head_dim = int(_tf_config_get(model_config, "head_dim", 128))
self.intermediate_size = int(_tf_config_get(model_config, "intermediate_size", 12288))
self.vocab_size = int(_tf_config_get(model_config, "vocab_size", 151936))
self.rms_norm_eps = float(_tf_config_get(model_config, "rms_norm_eps", 1e-6))
self.rope_theta = float(_tf_config_get(model_config, "rope_theta", 5_000_000))
self.mrope_section = list(rope_scaling.get("mrope_section", [24, 20, 20]))
self.rms_norm_eps = self._resolve_rms_norm_eps(model_config)
self.rope_theta = self._resolve_rope_theta(model_config)
self.mrope_section = self._resolve_mrope_section(model_config)
self.qk_norm_for_diffusion = bool(_tf_config_get(model_config, "qk_norm_for_diffusion", True))
self.latent_patch_size = int(_tf_config_get(model_config, "latent_patch_size", 2))
self.latent_channel_size = int(_tf_config_get(model_config, "latent_channel", 48))
self.timestep_scale = float(_tf_config_get(model_config, "timestep_scale", 0.001))
@@ -1095,10 +1123,12 @@ class Cosmos3VFMTransformer(nn.Module):
)
self.patch_latent_dim = (self.latent_patch_size**2) * self.latent_channel_size
self.use_k_norm_und_for_gen = _tf_config_get(model_config, "use_k_norm_und_for_gen", None)
dtype = od_config.dtype
quant_config = getattr(od_config, "quantization_config", None) if od_config else None
self.language_model = Cosmos3LanguageModel(
self.language_model = self._language_model_cls(
hidden_size=self.hidden_size,
intermediate_size=self.intermediate_size,
num_hidden_layers=self.num_hidden_layers,
@@ -1111,12 +1141,13 @@ class Cosmos3VFMTransformer(nn.Module):
mrope_section=self.mrope_section,
quant_config=quant_config,
prefix="language_model",
**self._language_model_kwargs(),
)
# Video projection layers are small; not worth quantizing.
self.proj_in = nn.Linear(self.patch_latent_dim, self.hidden_size)
self.proj_out = nn.Linear(self.hidden_size, self.patch_latent_dim)
self.time_embedder = TimestepEmbedder(self.hidden_size, target_dtype=dtype)
self.time_embedder = TimestepEmbedder(self.hidden_size)
if self.action_gen:
self.action_proj_in = DomainAwareLinear(
self.action_dim,
@@ -1147,6 +1178,8 @@ class Cosmos3VFMTransformer(nn.Module):
head_dim=self.head_dim,
rms_norm_eps=self.rms_norm_eps,
quant_config=quant_config,
mlp_cls=self._gen_mlp_cls,
qk_norm=self.qk_norm_for_diffusion,
prefix=f"gen_layers.{i}",
)
for i in range(self.num_hidden_layers)
@@ -0,0 +1,396 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Cosmos3 Edge transformer variant with a Nemotron dense UND backbone."""
from __future__ import annotations
from typing import Any
import torch
import torch.nn as nn
import torch.nn.functional as F
from vllm.distributed import get_tensor_model_parallel_world_size
from vllm.model_executor.layers.linear import ColumnParallelLinear, RowParallelLinear
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
from vllm_omni.diffusion.attention.layer import Attention as FrameworkAttention
from .transformer_cosmos3 import (
Cosmos3VFMTransformer,
Qwen3VLTextRotaryEmbedding,
RMSNorm,
_apply_rotary_pos_emb,
_nested_get,
_tf_config_get,
)
COSMOS3_EDGE_BACKBONE_TYPE = "cosmos3_edge_nemotron_dense"
def _tf_config_nested_get(config: Any, key: str, default: Any = None) -> Any:
if config is None:
return default
if isinstance(config, dict):
value = _nested_get(config, key)
elif hasattr(config, "to_dict"):
value = _nested_get(config.to_dict(), key)
elif hasattr(config, "params"):
value = _nested_get(config.params, key)
else:
value = getattr(config, key, None)
return default if value is None else value
class Cosmos3Relu2MLP(nn.Module):
"""Nemotron dense MLP: down_proj(relu(up_proj(x)) ** 2)."""
def __init__(
self,
hidden_size: int,
intermediate_size: int,
quant_config: QuantizationConfig | None = None,
prefix: str = "",
) -> None:
super().__init__()
self.up_proj = ColumnParallelLinear(
hidden_size,
intermediate_size,
bias=False,
gather_output=False,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.up_proj",
)
self.down_proj = RowParallelLinear(
intermediate_size,
hidden_size,
bias=False,
input_is_parallel=True,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.down_proj",
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
hidden = F.relu(self.up_proj(x))
return self.down_proj(hidden * hidden)
class Cosmos3EdgeCausalAttention(nn.Module):
"""Edge UND causal attention.
Reasoner self-attention uses raw UND Q/K with RoPE because
``qk_norm_for_text=false``. The GEN-facing UND key cache is a separate
normalized-then-RoPE'd view of the raw K tensor when GEN QK norm is enabled;
no-GEN-QK-norm checkpoints reuse the raw RoPE'd UND K.
"""
def __init__(
self,
*,
hidden_size: int,
num_attention_heads: int,
num_key_value_heads: int,
head_dim: int,
rms_norm_eps: float,
use_k_norm_und_for_gen: bool,
quant_config: QuantizationConfig | None = None,
prefix: str = "",
) -> None:
super().__init__()
self.hidden_size = hidden_size
self.num_heads = num_attention_heads
self.num_kv_heads = num_key_value_heads
self.head_dim = head_dim
tp_size = get_tensor_model_parallel_world_size()
self.num_heads_local = self.num_heads // tp_size
self.num_kv_heads_local = self.num_kv_heads // tp_size
self.to_q = ColumnParallelLinear(
hidden_size,
self.num_heads * self.head_dim,
bias=False,
gather_output=False,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.to_q",
)
self.to_k = ColumnParallelLinear(
hidden_size,
self.num_kv_heads * self.head_dim,
bias=False,
gather_output=False,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.to_k",
)
self.to_v = ColumnParallelLinear(
hidden_size,
self.num_kv_heads * self.head_dim,
bias=False,
gather_output=False,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.to_v",
)
self.to_out = RowParallelLinear(
self.num_heads * self.head_dim,
hidden_size,
bias=False,
input_is_parallel=True,
return_bias=False,
quant_config=quant_config,
prefix=f"{prefix}.to_out",
)
self.k_norm_und_for_gen = RMSNorm(self.head_dim, eps=rms_norm_eps) if use_k_norm_und_for_gen else None
self.attn = FrameworkAttention(
num_heads=self.num_heads,
head_size=self.head_dim,
causal=True,
softmax_scale=1.0 / (self.head_dim**0.5),
num_kv_heads=self.num_kv_heads,
skip_sequence_parallel=True,
)
def forward(
self,
hidden_states: torch.Tensor,
freqs_cos: torch.Tensor,
freqs_sin: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
B, S, _ = hidden_states.shape
q = self.to_q(hidden_states).view(B, S, self.num_heads_local, self.head_dim)
k = self.to_k(hidden_states).view(B, S, self.num_kv_heads_local, self.head_dim)
v = self.to_v(hidden_states).view(B, S, self.num_kv_heads_local, self.head_dim)
q_reasoner, k_reasoner = _apply_rotary_pos_emb(q, k, freqs_cos, freqs_sin)
if self.k_norm_und_for_gen is not None:
k_gen = F.rms_norm(
k,
(self.head_dim,),
self.k_norm_und_for_gen.weight,
eps=self.k_norm_und_for_gen.variance_epsilon,
)
_, k_gen = _apply_rotary_pos_emb(q, k_gen, freqs_cos, freqs_sin)
else:
k_gen = k_reasoner
out = self.attn(q_reasoner, k_reasoner, v).reshape(B, S, -1)
return self.to_out(out), k_gen, v
class Cosmos3EdgeUndDecoderLayer(nn.Module):
"""Edge UND decoder layer: causal self-attention + ReLU2 MLP."""
def __init__(
self,
*,
hidden_size: int,
intermediate_size: int,
num_attention_heads: int,
num_key_value_heads: int,
head_dim: int,
rms_norm_eps: float,
use_k_norm_und_for_gen: bool,
quant_config: QuantizationConfig | None = None,
prefix: str = "",
) -> None:
super().__init__()
self.self_attn = Cosmos3EdgeCausalAttention(
hidden_size=hidden_size,
num_attention_heads=num_attention_heads,
num_key_value_heads=num_key_value_heads,
head_dim=head_dim,
rms_norm_eps=rms_norm_eps,
use_k_norm_und_for_gen=use_k_norm_und_for_gen,
quant_config=quant_config,
prefix=f"{prefix}.self_attn",
)
self.input_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps)
self.post_attention_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps)
self.mlp = Cosmos3Relu2MLP(
hidden_size=hidden_size,
intermediate_size=intermediate_size,
quant_config=quant_config,
prefix=f"{prefix}.mlp",
)
def forward(
self,
hidden_states: torch.Tensor,
freqs: tuple[torch.Tensor, torch.Tensor],
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
residual = hidden_states
hidden_states = self.input_layernorm(hidden_states)
cos, sin = freqs
attn_out, k, v = self.self_attn(hidden_states, cos, sin)
hidden_states = residual + attn_out
residual = hidden_states
hidden_states = self.post_attention_layernorm(hidden_states)
hidden_states = residual + self.mlp(hidden_states)
return hidden_states, k, v
class Cosmos3EdgeLanguageModel(nn.Module):
"""Nemotron dense UND tower that returns GEN-facing K plus raw V per layer."""
_layerwise_offload_blocks_attrs = ["layers"]
def __init__(
self,
*,
hidden_size: int,
intermediate_size: int,
num_hidden_layers: int,
num_attention_heads: int,
num_key_value_heads: int,
head_dim: int,
vocab_size: int,
rms_norm_eps: float,
rope_theta: float,
mrope_section: list[int],
use_k_norm_und_for_gen: bool,
quant_config: QuantizationConfig | None = None,
prefix: str = "",
) -> None:
super().__init__()
self.embed_tokens = nn.Embedding(vocab_size, hidden_size)
self.rotary_emb = Qwen3VLTextRotaryEmbedding(
head_dim=head_dim,
rope_theta=rope_theta,
mrope_section=mrope_section,
)
self.layers = nn.ModuleList(
[
Cosmos3EdgeUndDecoderLayer(
hidden_size=hidden_size,
intermediate_size=intermediate_size,
num_attention_heads=num_attention_heads,
num_key_value_heads=num_key_value_heads,
head_dim=head_dim,
rms_norm_eps=rms_norm_eps,
use_k_norm_und_for_gen=use_k_norm_und_for_gen,
quant_config=quant_config,
prefix=f"{prefix}.layers.{i}",
)
for i in range(num_hidden_layers)
]
)
self.norm = RMSNorm(hidden_size, eps=rms_norm_eps)
def forward(
self,
text_ids: torch.Tensor,
freqs: tuple[torch.Tensor, torch.Tensor],
) -> list[tuple[torch.Tensor, torch.Tensor]]:
hidden = self.embed_tokens(text_ids)
cached_kv: list[tuple[torch.Tensor, torch.Tensor]] = []
for layer in self.layers:
hidden, k, v = layer(hidden, freqs)
cached_kv.append((k, v))
return cached_kv
class Cosmos3EdgeVFMTransformer(Cosmos3VFMTransformer):
"""Cosmos3 Edge variant with Nemotron dense UND and shared GEN diffusion."""
_language_model_cls = Cosmos3EdgeLanguageModel
_gen_mlp_cls = Cosmos3Relu2MLP
@staticmethod
def _validate_supported_config(model_config: Any) -> None:
expected_values = {
"qk_norm_for_text": False,
"position_embedding_type": "unified_3d_mrope",
"unified_3d_mrope_reset_spatial_ids": True,
"joint_attn_implementation": "two_way",
}
for key, expected in expected_values.items():
actual = _tf_config_get(model_config, key, expected)
if actual != expected:
raise ValueError(
f"Unsupported Cosmos3 Edge transformer config: {key}={actual!r}; expected {expected!r}."
)
backbone_type = _tf_config_get(model_config, "backbone_type", None)
if backbone_type is None:
raise ValueError(
f"Cosmos3 Edge transformer config must declare backbone_type={COSMOS3_EDGE_BACKBONE_TYPE!r}."
)
if backbone_type != COSMOS3_EDGE_BACKBONE_TYPE:
raise ValueError(
"Unsupported Cosmos3 Edge transformer config: "
f"backbone_type={backbone_type!r}; expected {COSMOS3_EDGE_BACKBONE_TYPE!r}."
)
required_values = {
"latent_channel": 48,
"latent_patch_size": 2,
"temporal_compression_factor": 4,
}
for key, expected in required_values.items():
actual = _tf_config_get(model_config, key, None)
if actual is None:
raise ValueError(f"Cosmos3 Edge transformer config must declare {key}={expected}.")
if int(actual) != expected:
raise ValueError(
f"Unsupported Cosmos3 Edge transformer config: {key}={actual!r}; expected {expected!r}."
)
@classmethod
def _resolve_rms_norm_eps(cls, model_config: Any) -> float:
return float(
_tf_config_get(
model_config,
"rms_norm_eps",
_tf_config_get(model_config, "layer_norm_epsilon", 1e-5),
)
)
@classmethod
def _resolve_rope_theta(cls, model_config: Any) -> float:
return float(_tf_config_get(model_config, "rope_theta", 100_000_000))
@classmethod
def _resolve_mrope_section(cls, model_config: Any) -> list[int]:
mrope_section = _tf_config_get(model_config, "mrope_section", None)
if mrope_section is None:
mrope_section = _tf_config_nested_get(model_config, "mrope_section", [24, 20, 20])
return list(mrope_section)
def _language_model_kwargs(self) -> dict[str, Any]:
return {"use_k_norm_und_for_gen": self.use_k_norm_und_for_gen}
def validate_loaded_weights(self, loaded: set[str]) -> None:
missing: list[str] = []
for layer_idx in range(self.num_hidden_layers):
required_markers = (
f"language_model.layers.{layer_idx}.mlp.up_proj.",
f"language_model.layers.{layer_idx}.mlp.down_proj.",
f"gen_layers.{layer_idx}.mlp.up_proj.",
f"gen_layers.{layer_idx}.mlp.down_proj.",
)
if self.use_k_norm_und_for_gen:
required_markers = (
*required_markers,
f"language_model.layers.{layer_idx}.self_attn.k_norm_und_for_gen.",
)
missing.extend(
marker.rstrip(".") for marker in required_markers if not any(marker in name for name in loaded)
)
if missing:
preview = ", ".join(missing[:10])
suffix = " ..." if len(missing) > 10 else ""
raise ValueError(
"Cosmos3 Edge transformer checkpoint is missing required weights for "
f"{preview}{suffix}. Use a converted Edge checkpoint with ReLU2 UND/GEN MLP weights."
)
+8
View File
@@ -276,6 +276,11 @@ _DIFFUSION_MODELS = {
"pipeline_cosmos3",
"Cosmos3OmniDiffusersPipeline",
),
"Cosmos3OmniPipeline": (
"cosmos3",
"pipeline_cosmos3",
"Cosmos3OmniDiffusersPipeline",
),
"SoulXSingerPipeline": (
"soulx_singer",
"pipeline_soulx_singer_svs",
@@ -536,6 +541,7 @@ _DIFFUSION_POST_PROCESS_FUNCS = {
"DreamIDOmniPipeline": "get_dreamid_omni_post_process_func",
"SenseNovaU1Pipeline": "get_sensenova_u1_post_process_func",
"Cosmos3OmniDiffusersPipeline": "get_cosmos3_post_process_func",
"Cosmos3OmniPipeline": "get_cosmos3_post_process_func",
"HiDreamImagePipeline": "get_hidream_image_post_process_func",
"StableDiffusionXLPipeline": "get_sdxl_image_post_process_func",
"Krea2Pipeline": "get_krea2_post_process_func",
@@ -546,6 +552,7 @@ _DIFFUSION_IR_OP_PRIORITY_FUNCS = {
# `ir_op_priority_func` function must be placed in {mod_folder}/{mod_relname}.py,
# where mod_folder and mod_relname are defined and mapped using `_DIFFUSION_MODELS` via the `arch` key.
"Cosmos3OmniDiffusersPipeline": "get_cosmos3_ir_op_priority_func",
"Cosmos3OmniPipeline": "get_cosmos3_ir_op_priority_func",
}
_DIFFUSION_PRE_PROCESS_FUNCS = {
@@ -570,6 +577,7 @@ _DIFFUSION_PRE_PROCESS_FUNCS = {
"HunyuanImage3ForCausalMM": "get_hunyuan_image_3_pre_process_func",
"MagiHumanPipeline": "get_magi_human_pre_process_func",
"Cosmos3OmniDiffusersPipeline": "get_cosmos3_pre_process_func",
"Cosmos3OmniPipeline": "get_cosmos3_pre_process_func",
"SoulXSingerPipeline": "get_soulxsinger_pre_process_func",
"SoulXSingerSVCPipeline": "get_soulxsinger_svc_pre_process_func",
}
+5
View File
@@ -153,6 +153,11 @@ _EXTRA_SPECS: dict[str, dict[str, Any]] = {
"extra_output_params": COSMOS3_EXTRA_OUTPUT_PARAMS,
"text_to_image_prompt_builder": build_cosmos3_text_to_image_prompt,
},
"Cosmos3OmniPipeline": {
"extra_body_params": COSMOS3_EXTRA_BODY_PARAMS,
"extra_output_params": COSMOS3_EXTRA_OUTPUT_PARAMS,
"text_to_image_prompt_builder": build_cosmos3_text_to_image_prompt,
},
"MagiHumanPipeline": {
"extra_body_params": MAGI_HUMAN_EXTRA_BODY_PARAMS,
"extra_output_params": MAGI_HUMAN_EXTRA_OUTPUT_PARAMS,