[Rebase] Align vLLM-Omni with vLLM v0.31.0 (#8459)

Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
Zhou Taichang authored and GitHub committed 2026-10-05 14:11:01 +08:00
1 parent 1f47fa2f29
commit 0b0d2d7009
141 files changed
+2885 -721

No files matched your search

+3 -1
View File
@@ -329,7 +329,9 @@ def _uneven_cfg_sharded_worker(
init_method=init_method,
rank=rank,
world_size=4,
timeout=datetime.timedelta(seconds=10),
# Spawned workers import torch/vLLM before joining; allow startup
# skew without changing the collective deadlock checks below.
timeout=datetime.timedelta(seconds=60),
)
try:
fs_groups = [
@@ -103,7 +103,7 @@ def test_block_hashing_uses_upstream_extra_keys_directly(monkeypatch):
# Independent expected native extras: a crossing item has a negative
# relative offset; a block can include the end of one item and the next.
first, second = (feature.identifier for feature in row.mm_features)
extras = [((first, 2),), ((first, -2), (second, 3)), ((second, -1),)]
extras = [(("mm", first, 2),), (("mm", first, -2), ("mm", second, 3)), (("mm", second, -1),)]
parent = BlockHash(hash_fn(("diffusion-prefix-root-v1", row.cache_namespace)))
expected = []
for start, keys in zip(range(0, 12, 4), extras, strict=True):
@@ -31,6 +31,10 @@ def producer(monkeypatch):
# vLLM 0.30 send_kv_to_decode also reads the PP size (ce08bb5b34,
# #56033), the MLA/Mamba flags and the head-resharding validator.
worker.pp_size = 1
worker.opaque_packed_storages = set()
worker._prepared_transfer_regions = {}
worker.region_shared_groups = []
worker.region_row_offsets = []
worker.use_mla = False
worker.kv_cache_config = SimpleNamespace(has_mamba_layers=False)
worker._validate_head_resharding_layout = Mock(return_value=None)
@@ -1253,14 +1253,15 @@ def test_layer_adapter_accepts_platform_native_backend_and_uses_rank_local_heads
speculative_config=None,
)
def select_backend(*, backend, attn_selector_config, num_heads):
# Exercise the real 0.29 selector, replacing only platform resolution.
def select_backend(*, backend, attn_selector_config, num_heads, _run_kv_cache_dtype):
# Exercise the real upstream selector, replacing only platform resolution.
# Ulysses has already gathered tokens and sharded heads before the
# native kernel; the MoE PCP mapping must not request PCP attention.
assert not attn_selector_config.use_pcp
assert not attn_selector_config.use_dcp
assert attn_selector_config.use_non_causal
assert num_heads == 4
assert _run_kv_cache_dtype == config.cache_config.cache_dtype
selected_backends.append((backend, config.attention_config.backend_per_kind))
if selection_fails:
raise ValueError("test backend unavailable")
+15 -4
View File
@@ -14,6 +14,7 @@ from typing import Literal
import pytest
import torch
from tests.helpers.distributed import configure_cpu_collective_worker, start_collective_workers
from tests.helpers.mark import hardware_marks
from vllm_omni.diffusion.distributed.comm import RingComm, SeqAllToAll4D, SeqAllToAll5D
from vllm_omni.diffusion.distributed.parallel_state import (
@@ -56,6 +57,8 @@ def _init_worker(
"MASTER_PORT": str(master_port),
}
)
if device_kind == "cpu":
configure_cpu_collective_worker()
backend = "gloo" if device_kind == "cpu" else None
init_distributed_environment(backend=backend)
@@ -227,6 +230,10 @@ def _run_ring_p2p(
atol=atol,
msg=f"[Rank {local_rank}] Ring P2P data mismatch",
)
if device_kind == "cpu":
# P2P completion only synchronizes neighbors. Keep fast CPU ranks
# alive until every rank has finished constructing and using groups.
torch.distributed.barrier()
finally:
destroy_distributed_env()
@@ -243,7 +250,7 @@ def _spawn_4d_identity(
device_kind: DeviceKind,
master_port: int,
) -> None:
torch.multiprocessing.spawn(
start_collective_workers(
_run_4d_identity,
args=(
world_size,
@@ -257,6 +264,7 @@ def _spawn_4d_identity(
master_port,
),
nprocs=world_size,
device_kind=device_kind,
)
@@ -272,7 +280,7 @@ def _spawn_5d_identity(
device_kind: DeviceKind,
master_port: int,
) -> None:
torch.multiprocessing.spawn(
start_collective_workers(
_run_5d_identity,
args=(
world_size,
@@ -286,6 +294,7 @@ def _spawn_5d_identity(
master_port,
),
nprocs=world_size,
device_kind=device_kind,
)
@@ -299,10 +308,11 @@ def _spawn_ring_p2p(
device_kind: DeviceKind,
master_port: int,
) -> None:
torch.multiprocessing.spawn(
start_collective_workers(
_run_ring_p2p,
args=(world_size, dtype, batch_size, num_heads, head_size, device_kind, master_port),
nprocs=world_size,
device_kind=device_kind,
)
@@ -499,8 +509,9 @@ def _run_fused_qkv_matches_three_4d(
@pytest.mark.cpu
@pytest.mark.parametrize("world_size", [2, 4])
def test_fused_qkv_all_to_all_matches_three_4d_exchanges_cpu(world_size: int) -> None:
torch.multiprocessing.spawn(
start_collective_workers(
_run_fused_qkv_matches_three_4d,
args=(world_size, "cpu", 29613),
nprocs=world_size,
device_kind="cpu",
)
@@ -19,6 +19,7 @@ from vllm.utils.network_utils import get_file_store_init_method
from vllm.v1.worker.gpu_worker import AsyncIntermediateTensors
import vllm_omni.diffusion.distributed.pipeline_parallel as pp_module
from tests.helpers.distributed import collective_context, start_collective_workers
from tests.helpers.mark import hardware_marks
from vllm_omni.diffusion.distributed.cfg_parallel import CFGParallelMixin
from vllm_omni.diffusion.distributed.parallel_state import (
@@ -39,6 +40,25 @@ DeviceKind = Literal["cpu", "cuda"]
_UNIT_MARKS = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu]
@pytest.fixture(autouse=True)
def _restore_collective_platforms():
"""CPU baselines run in the parent process; retain its original platforms."""
import vllm.platforms
from vllm_omni.diffusion.distributed import comm, group_coordinator, parallel_state
original_platform = vllm.platforms.current_platform
original_omni_platforms = {
module: module.current_omni_platform for module in (comm, group_coordinator, parallel_state)
}
try:
yield
finally:
vllm.platforms.current_platform = original_platform
for module, platform in original_omni_platforms.items():
module.current_omni_platform = platform
def _cleanup_distributed() -> None:
"""Destroy omni distributed state left dangling after a test.
@@ -409,6 +429,10 @@ def init_dist(
device = _worker_device(local_rank, device_kind)
if device_kind == "cuda":
current_omni_platform.set_device(device)
else:
from tests.helpers.distributed import configure_cpu_collective_worker
configure_cpu_collective_worker()
backend = "gloo" if device_kind == "cpu" else None
init_distributed_environment(
world_size=world_size,
@@ -500,13 +524,14 @@ def isend_irecv_worker(
def _run_isend_irecv(pp_size: int, device_kind: DeviceKind, init_method: str) -> None:
mp_context = torch.multiprocessing.get_context("spawn")
mp_context = collective_context(device_kind)
manager = mp_context.Manager()
q = manager.Queue()
torch.multiprocessing.spawn(
start_collective_workers(
isend_irecv_worker,
args=(pp_size, init_method, device_kind, q),
nprocs=pp_size,
device_kind=device_kind,
)
results = {label: tensor for label, tensor in [q.get(), q.get()]}
torch.testing.assert_close(
@@ -656,15 +681,16 @@ def _run_predict_noise(
baseline_out = compute_single_gpu_baseline(test_config, dtype, do_true_cfg, device_kind)
mp_context = torch.multiprocessing.get_context("spawn")
mp_context = collective_context(device_kind)
manager = mp_context.Manager()
pp_q = manager.Queue()
world_size = pp_size * cfg_size
torch.multiprocessing.spawn(
start_collective_workers(
predict_noise_worker,
args=(world_size, init_method, pp_size, cfg_size, do_true_cfg, dtype, test_config, device_kind, pp_q),
nprocs=world_size,
device_kind=device_kind,
)
pp_out = pp_q.get()
@@ -900,15 +926,16 @@ def _run_scheduler_step(
baseline = compute_scheduler_step_baseline(test_config, do_true_cfg, device_kind)
mp_context = torch.multiprocessing.get_context("spawn")
mp_context = collective_context(device_kind)
manager = mp_context.Manager()
q = manager.Queue()
world_size = pp_size * cfg_size
torch.multiprocessing.spawn(
start_collective_workers(
scheduler_step_worker,
args=(world_size, init_method, pp_size, cfg_size, do_true_cfg, test_config, device_kind, q),
nprocs=world_size,
device_kind=device_kind,
)
result = q.get()
@@ -239,11 +239,11 @@ def test_constructor_rejects_unsupported_mllm_quantization(mock_dependencies, mo
)
def test_constructor_routes_mllm_quantization(mock_dependencies, quantization_config, quantize_mllm):
from transformers import AutoModel
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm.model_executor.layers.quantization.online.base import OnlineQuantizationConfig
from vllm.model_executor.layers.quantization.utils.quant_utils import kFp8Static128BlockSym
from vllm_omni.diffusion.models.boogu_image.pipeline_boogu_image import BooguImagePipeline
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
od_config = OmniDiffusionConfig(
model="dummy-boogu",
@@ -287,15 +287,14 @@ def test_constructor_routes_mllm_quantization(mock_dependencies, quantization_co
mock_dependencies["mllm_config_loader"].assert_not_called()
mock_dependencies["mllm_builder"].assert_not_called()
assert [source.subfolder for source in pipeline.weights_sources] == ["transformer"]
assert isinstance(mock_dependencies["transformer_cls"].call_args.kwargs["quant_config"], Fp8Config)
assert isinstance(mock_dependencies["transformer_cls"].call_args.kwargs["quant_config"], DiffusionFp8Config)
def test_constructor_preserves_mllm_ignored_layers(mock_dependencies):
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.diffusion.models.boogu_image.pipeline_boogu_image import BooguImagePipeline
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
quant_config = Fp8Config(
quant_config = DiffusionFp8Config(
ignored_layers=["mllm.language_model.layers.0.self_attn.q_proj", "transformer.blocks.0.attn.to_q"]
)
od_config = OmniDiffusionConfig(
@@ -16,17 +16,13 @@ class _RecordingBlock(nn.Module):
def test_flux2_propagates_online_quant_config_to_all_transformer_blocks(monkeypatch):
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.diffusion.models.flux2 import flux2_transformer
from vllm_omni.quantization import build_quant_config
monkeypatch.setattr(flux2_transformer, "Flux2TransformerBlock", _RecordingBlock)
monkeypatch.setattr(flux2_transformer, "Flux2SingleTransformerBlock", _RecordingBlock)
quant_config = Fp8Config(
is_checkpoint_fp8_serialized=False,
activation_scheme="dynamic",
)
quant_config = build_quant_config("fp8")
model = flux2_transformer.Flux2Transformer2DModel(
in_channels=4,
out_channels=4,
@@ -71,12 +71,12 @@ def _small_od_config(*, class_name=None):
def test_fp8_scope_and_prefix_propagation(monkeypatch):
from vllm.model_executor.layers.linear import LinearBase, UnquantizedLinearMethod
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm.model_executor.layers.quantization.utils.quant_utils import (
is_layer_skipped,
)
from vllm_omni.diffusion.models.minimax_h3 import minimax_h3_transformer as h3
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
monkeypatch.setattr(h3, "ColumnParallelLinear", _FakeLinear)
monkeypatch.setattr(h3, "MergedColumnParallelLinear", _FakeLinear)
@@ -92,7 +92,7 @@ def test_fp8_scope_and_prefix_propagation(monkeypatch):
"blocks.0.adaln_proj.linear",
"final_layer.adaln_proj.linear",
}
fp8_config = Fp8Config(ignored_layers=sorted(ignored_layers))
fp8_config = DiffusionFp8Config(ignored_layers=sorted(ignored_layers))
model = h3.MiniMaxH3DiTModel(
_small_od_config(),
quant_config=fp8_config,
+78 -13
View File
@@ -15,6 +15,74 @@ def test_build_quant_config_fp8():
assert config is not None
assert config.get_name() == "fp8"
assert config.activation_scheme == "dynamic"
assert not config.is_checkpoint_fp8_serialized
def test_build_quant_config_fp8_uses_native_online_quantization():
from vllm.model_executor.layers.quantization.online.base import OnlineQuantizationConfig
from vllm.model_executor.layers.quantization.utils.quant_utils import kFp8StaticTensorSym
from vllm_omni.quantization import build_quant_config
config = build_quant_config("fp8", ignored_layers=["proj_out"])
assert isinstance(config, OnlineQuantizationConfig)
assert config.args.linear.weight == kFp8StaticTensorSym
assert config.args.moe.weight == kFp8StaticTensorSym
assert config.ignored_layers == ["proj_out"]
def test_build_quant_config_upstream_online_fp8_shorthand():
from vllm.model_executor.layers.quantization.online.base import OnlineQuantizationConfig
from vllm.model_executor.layers.quantization.utils.quant_utils import kFp8StaticTensorSym
from vllm_omni.quantization import build_quant_config
config = build_quant_config("fp8_per_tensor", ignore=["proj_out"])
assert isinstance(config, OnlineQuantizationConfig)
assert config.args.linear.weight == kFp8StaticTensorSym
assert config.ignored_layers == ["proj_out"]
def test_online_fp8_rejects_static_activation():
from vllm_omni.quantization import build_quant_config
with pytest.raises(ValueError, match="activation_scheme='dynamic'"):
build_quant_config("fp8", activation_scheme="static")
def test_build_online_quant_config_preserves_explicit_native_args():
from vllm.config.quantization import QuantizationConfigArgs, QuantSpec
from vllm.model_executor.layers.quantization.online.base import OnlineQuantizationConfig
from vllm_omni.quantization import build_quant_config
args = QuantizationConfigArgs(linear=QuantSpec(weight="fp8_per_tensor_static"), ignore=["proj_out"])
config = build_quant_config("online", args=args)
assert isinstance(config, OnlineQuantizationConfig)
assert config.args is args
assert config.ignored_layers == ["proj_out"]
def test_build_online_quant_config_requires_args():
from vllm_omni.quantization import build_quant_config
with pytest.raises(ValueError, match="requires quantization config arguments"):
build_quant_config("online")
def test_serialized_checkpoint_replaces_online_fp8_config():
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.quantization import build_quant_config
from vllm_omni.quantization.factory import resolve_quant_config_from_disk
config = resolve_quant_config_from_disk(
build_quant_config("fp8"),
{"quant_method": "fp8", "is_checkpoint_fp8_serialized": True, "activation_scheme": "static"},
)
assert isinstance(config, Fp8Config)
assert config.is_checkpoint_fp8_serialized
assert config.activation_scheme == "static"
def test_build_quant_config_none():
@@ -39,7 +107,7 @@ def test_build_quant_config_invalid():
def test_build_quant_config_dict():
from vllm_omni.quantization import build_quant_config
config = build_quant_config({"method": "fp8", "activation_scheme": "static"})
config = build_quant_config({"method": "fp8", "is_checkpoint_fp8_serialized": True, "activation_scheme": "static"})
assert config is not None
assert config.get_name() == "fp8"
assert config.activation_scheme == "static"
@@ -48,7 +116,7 @@ def test_build_quant_config_dict():
def test_build_quant_config_dict_not_mutated():
from vllm_omni.quantization import build_quant_config
original = {"method": "fp8", "activation_scheme": "static"}
original = {"method": "fp8", "is_checkpoint_fp8_serialized": True, "activation_scheme": "static"}
copy = original.copy()
build_quant_config(original)
assert original == copy
@@ -99,7 +167,7 @@ def test_build_quant_config_per_component_inner_dict_not_mutated():
"""Inner component dicts should not be mutated by build_quant_config."""
from vllm_omni.quantization import build_quant_config
inner = {"method": "fp8", "activation_scheme": "static"}
inner = {"method": "fp8", "is_checkpoint_fp8_serialized": True, "activation_scheme": "static"}
original = inner.copy()
build_quant_config({"transformer": inner, "vae": None})
assert inner == original
@@ -115,20 +183,18 @@ def test_flat_dict_not_misdetected_as_per_component():
def test_build_quant_config_passthrough():
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.quantization import build_quant_config
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
fp8 = Fp8Config(is_checkpoint_fp8_serialized=False, activation_scheme="dynamic")
fp8 = DiffusionFp8Config()
assert build_quant_config(fp8) is fp8
def test_component_config_routing():
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.quantization import ComponentQuantizationConfig
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
fp8 = Fp8Config(is_checkpoint_fp8_serialized=False, activation_scheme="dynamic")
fp8 = DiffusionFp8Config()
config = ComponentQuantizationConfig(
component_configs={"transformer": fp8, "vae": None},
)
@@ -140,11 +206,10 @@ def test_component_config_routing():
def test_component_config_with_default():
from vllm.model_executor.layers.quantization.fp8 import Fp8Config
from vllm_omni.quantization import ComponentQuantizationConfig
from vllm_omni.quantization.fp8_config import DiffusionFp8Config
fp8 = Fp8Config(is_checkpoint_fp8_serialized=False, activation_scheme="dynamic")
fp8 = DiffusionFp8Config()
config = ComponentQuantizationConfig(
component_configs={"vae": None},
default_config=fp8,
@@ -167,7 +232,7 @@ def test_integration_dict():
config = OmniDiffusionConfig(
model="test",
quantization_config={"method": "fp8", "activation_scheme": "static"},
quantization_config={"method": "fp8", "is_checkpoint_fp8_serialized": True, "activation_scheme": "static"},
)
assert config.quantization_config is not None
assert config.quantization_config.get_name() == "fp8"
@@ -0,0 +1,72 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Exercise online FP8 loading and inference through the native CUDA path."""
import pytest
import torch
from torch.nn import functional as F
from vllm.config import set_current_vllm_config
from vllm.distributed import (
destroy_distributed_environment,
destroy_model_parallel,
init_distributed_environment,
initialize_model_parallel,
)
from vllm.model_executor.layers.linear import ReplicatedLinear, UnquantizedLinearMethod
from vllm.model_executor.layers.quantization.online.fp8 import Fp8PerTensorOnlineLinearMethod
from vllm.utils.network_utils import get_open_port
from vllm.utils.torch_utils import set_default_torch_dtype
from tests.helpers.mark import hardware_test
from vllm_omni.diffusion.data import OmniDiffusionConfig
from vllm_omni.diffusion.vllm_config import create_diffusion_vllm_config
from vllm_omni.quantization import build_quant_config
pytestmark = [pytest.mark.core_model, pytest.mark.diffusion]
@pytest.mark.parametrize("method", ["fp8", "fp8_per_tensor"])
@hardware_test(res={"cuda": "L4"}, num_cards=1)
def test_online_fp8_loads_bf16_weights_and_runs_native_kernel(method):
ignored_key = "ignored_layers" if method == "fp8" else "ignore"
config = build_quant_config(method, **{ignored_key: ["skip"]})
od_config = OmniDiffusionConfig(model="test", dtype=torch.bfloat16, quantization_config=config)
vllm_config = create_diffusion_vllm_config(torch.device("cuda:0"), od_config)
try:
init_distributed_environment(
world_size=1,
rank=0,
local_rank=0,
distributed_init_method=f"tcp://127.0.0.1:{get_open_port()}",
backend="nccl",
)
with set_current_vllm_config(vllm_config), set_default_torch_dtype(torch.bfloat16), torch.device("cuda:0"):
initialize_model_parallel(tensor_model_parallel_size=1, pipeline_model_parallel_size=1)
layer = ReplicatedLinear(
128, 128, bias=False, params_dtype=torch.bfloat16, quant_config=config, prefix="linear", disable_tp=True
)
assert isinstance(layer.quant_method, Fp8PerTensorOnlineLinearMethod)
assert layer.weight.is_meta
generator = torch.Generator(device="cuda").manual_seed(42)
weights = torch.randn(128, 128, generator=generator, dtype=torch.bfloat16) / 128**0.5
inputs = torch.randn(32, 128, generator=generator, dtype=torch.bfloat16)
expected = F.linear(inputs, weights).float()
# This is the loader installed by the native online quantizer.
# It materializes and quantizes the BF16 checkpoint in one pass.
layer.weight.weight_loader(layer.weight, weights)
assert layer.weight.dtype == torch.float8_e4m3fn
actual = layer(inputs)[0].float()
relative_mse = (actual - expected).square().mean() / expected.square().mean()
assert relative_mse.item() < 0.002
ignored = ReplicatedLinear(
128, 128, bias=False, params_dtype=torch.bfloat16, quant_config=config, prefix="skip", disable_tp=True
)
assert isinstance(ignored.quant_method, UnquantizedLinearMethod)
assert ignored.weight.dtype == torch.bfloat16
finally:
destroy_model_parallel()
destroy_distributed_environment()