[Model] Extend NPU support for HunyuanImage3 Diffusion Model (#1689)
Signed-off-by: ElleElleWu <1608928702@qq.com> Signed-off-by: gcanlin <canlinguosdu@gmail.com> Co-authored-by: skf1999 <13234016272@163.com> Co-authored-by: Just-it <1161406585@qq.com> Co-authored-by: Semmer2 <semmer@live.cn> Co-authored-by: gcanlin <canlinguosdu@gmail.com>
This commit is contained in:
11 files changed
+395
-27
No files matched your search
@@ -0,0 +1,112 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Unit tests for HunyuanFusedMoE (Support HunyuanImage3 Diffusion Model, 5a779b4)."""
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
class TestHunyuanFusedMoEPlatformDispatch:
|
||||
"""Test platform dispatch via platform qualname hooks."""
|
||||
|
||||
def test_default_platform_uses_default_impl_qualname(self, mocker):
|
||||
"""HunyuanFusedMoE should resolve the impl class from the platform hook."""
|
||||
import vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe as hunyuan_moe
|
||||
|
||||
mock_platform = mocker.MagicMock()
|
||||
mock_platform.get_diffusion_model_impl_qualname.return_value = (
|
||||
"vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe.HunyuanFusedMoEDefault"
|
||||
)
|
||||
|
||||
mocker.patch.object(
|
||||
hunyuan_moe,
|
||||
"current_omni_platform",
|
||||
mock_platform,
|
||||
)
|
||||
mock_resolve = mocker.patch.object(hunyuan_moe, "resolve_obj_by_qualname")
|
||||
mock_impl = mocker.MagicMock()
|
||||
mock_resolve.return_value = mock_impl
|
||||
|
||||
from vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe import (
|
||||
HunyuanFusedMoE,
|
||||
)
|
||||
|
||||
HunyuanFusedMoE(prefix="")
|
||||
|
||||
mock_platform.prepare_diffusion_op_runtime.assert_called_once_with("hunyuan_fused_moe")
|
||||
mock_platform.get_diffusion_model_impl_qualname.assert_called_once_with("hunyuan_fused_moe")
|
||||
mock_resolve.assert_called_once_with(
|
||||
"vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe.HunyuanFusedMoEDefault"
|
||||
)
|
||||
mock_impl.assert_called_once_with(prefix="")
|
||||
|
||||
|
||||
class TestHunyuanFusedMoEFactory:
|
||||
"""Test HunyuanFusedMoE factory __new__ and make_expert_params_mapping delegation."""
|
||||
|
||||
def test_new_delegates_to_impl_class(self, mocker):
|
||||
"""HunyuanFusedMoE(prefix=..., **kwargs) should instantiate and return impl instance."""
|
||||
import vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe as hunyuan_moe
|
||||
|
||||
class MockImpl:
|
||||
def __init__(self, *, prefix: str = "", **kwargs):
|
||||
self.prefix = prefix
|
||||
self.kwargs = kwargs
|
||||
|
||||
mock_platform = mocker.MagicMock()
|
||||
mock_platform.get_diffusion_model_impl_qualname.return_value = "mock.impl.Qualname"
|
||||
mocker.patch.object(hunyuan_moe, "current_omni_platform", mock_platform)
|
||||
|
||||
mock_impl_class = mocker.MagicMock(return_value=MockImpl(prefix="test", a=1))
|
||||
mocker.patch.object(hunyuan_moe, "resolve_obj_by_qualname", return_value=mock_impl_class)
|
||||
|
||||
from vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe import (
|
||||
HunyuanFusedMoE,
|
||||
)
|
||||
|
||||
result = HunyuanFusedMoE(prefix="test", a=1)
|
||||
|
||||
assert isinstance(result, MockImpl)
|
||||
assert result.prefix == "test"
|
||||
assert result.kwargs == {"a": 1}
|
||||
mock_platform.prepare_diffusion_op_runtime.assert_called_once_with("hunyuan_fused_moe")
|
||||
mock_platform.get_diffusion_model_impl_qualname.assert_called_once_with("hunyuan_fused_moe")
|
||||
mock_impl_class.assert_called_once_with(prefix="test", a=1)
|
||||
|
||||
def test_make_expert_params_mapping_delegates_to_impl(self, mocker):
|
||||
"""make_expert_params_mapping should delegate to impl class method."""
|
||||
import vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe as hunyuan_moe
|
||||
|
||||
expected_mapping = [("a", "b", 0, "c")]
|
||||
mock_platform = mocker.MagicMock()
|
||||
mock_platform.get_diffusion_model_impl_qualname.return_value = "mock.impl.Qualname"
|
||||
mocker.patch.object(hunyuan_moe, "current_omni_platform", mock_platform)
|
||||
|
||||
mock_impl_class = mocker.MagicMock()
|
||||
mock_impl_class.make_expert_params_mapping = mocker.MagicMock(return_value=expected_mapping)
|
||||
mocker.patch.object(hunyuan_moe, "resolve_obj_by_qualname", return_value=mock_impl_class)
|
||||
|
||||
from vllm_omni.diffusion.models.hunyuan_image_3.hunyuan_fused_moe import (
|
||||
HunyuanFusedMoE,
|
||||
)
|
||||
|
||||
result = HunyuanFusedMoE.make_expert_params_mapping(
|
||||
model=None,
|
||||
ckpt_gate_proj_name="gate",
|
||||
ckpt_down_proj_name="down",
|
||||
ckpt_up_proj_name="up",
|
||||
num_experts=4,
|
||||
num_redundant_experts=0,
|
||||
)
|
||||
|
||||
assert result == expected_mapping
|
||||
mock_platform.get_diffusion_model_impl_qualname.assert_called_once_with("hunyuan_fused_moe")
|
||||
mock_impl_class.make_expert_params_mapping.assert_called_once_with(
|
||||
None,
|
||||
ckpt_gate_proj_name="gate",
|
||||
ckpt_down_proj_name="down",
|
||||
ckpt_up_proj_name="up",
|
||||
num_experts=4,
|
||||
num_redundant_experts=0,
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Unit tests for OmniDiffusionConfig.is_moe (fix is_moe type and threshold, 6663c0b)."""
|
||||
|
||||
import pytest
|
||||
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig, TransformerConfig
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
class TestOmniDiffusionConfigIsMoE:
|
||||
"""Tests for OmniDiffusionConfig.is_moe property.
|
||||
|
||||
Covers commit 6663c0b: fix is_moe type and threshold
|
||||
- num_experts must be (list, tuple, int); otherwise return False.
|
||||
- Threshold: is_moe is True when num_experts > 0 (not > 1).
|
||||
"""
|
||||
|
||||
def test_is_moe_missing_num_experts_returns_false(self):
|
||||
"""When num_experts is absent, is_moe should be False."""
|
||||
tf_config = TransformerConfig.from_dict({})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
|
||||
def test_is_moe_none_num_experts_returns_false(self):
|
||||
"""When num_experts is explicitly None (e.g. in params), is_moe should be False."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": None})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
|
||||
def test_is_moe_non_allowed_type_returns_false(self):
|
||||
"""When num_experts is not int/list/tuple (e.g. str), is_moe should be False."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": "2"})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
|
||||
def test_is_moe_int_zero_returns_false(self):
|
||||
"""num_experts int 0 should yield is_moe False (threshold > 0)."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": 0})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
|
||||
def test_is_moe_int_one_returns_true(self):
|
||||
"""num_experts int 1 should yield is_moe True (threshold > 0, not > 1)."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": 1})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is True
|
||||
|
||||
def test_is_moe_int_gt_one_returns_true(self):
|
||||
"""num_experts int > 1 should yield is_moe True."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": 2})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is True
|
||||
|
||||
def test_is_moe_list_all_zero_returns_false(self):
|
||||
"""num_experts list with all <= 0 should yield is_moe False."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": [0]})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
|
||||
def test_is_moe_list_has_positive_returns_true(self):
|
||||
"""num_experts list with any int > 0 should yield is_moe True."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": [0, 1]})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is True
|
||||
|
||||
def test_is_moe_tuple_has_positive_returns_true(self):
|
||||
"""num_experts tuple with any int > 0 should yield is_moe True."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": (0, 2)})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is True
|
||||
|
||||
def test_is_moe_list_non_int_ignored(self):
|
||||
"""num_experts list with only non-int entries should yield is_moe False."""
|
||||
tf_config = TransformerConfig.from_dict({"num_experts": ["a", 0.0]})
|
||||
config = OmniDiffusionConfig(model="test", tf_model_config=tf_config)
|
||||
assert config.is_moe is False
|
||||
Reference in new issue
Block a user