[diffusion] CI: enable UT (#20690)

This commit is contained in:
Mick
2026-03-17 07:44:04 +08:00
committed by GitHub
parent 2ccdb7373e
commit 1eea744855
5 changed files with 95 additions and 21 deletions

View File

@@ -44,6 +44,7 @@ from sglang.multimodal_gen.utils import (
FlexibleArgumentParser,
StoreBoolean,
expand_path_fields,
expand_path_kwargs,
)
logger = init_logger(__name__)
@@ -932,6 +933,7 @@ class ServerArgs:
@classmethod
def from_dict(cls, kwargs: dict[str, Any]) -> "ServerArgs":
"""Create a ServerArgs object from a dictionary."""
kwargs = expand_path_kwargs(dict(kwargs))
attrs = [attr.name for attr in dataclasses.fields(cls)]
server_args_kwargs: dict[str, Any] = {}

View File

@@ -31,10 +31,10 @@ _UPDATE_WEIGHTS_MODEL_PAIR_IDS = (
SUITES = {
# no GPU required; safe to run on any CPU-only runner
"unit": [
"../unit/test_sampling_params_validate.py",
"../unit/test_sampling_params.py",
"../unit/test_storage.py",
"../unit/test_lora_format_adapter.py",
"../unit/test_server_args_unit.py",
"../unit/test_server_args.py",
# add new unit tests here
],
"1-gpu": [
@@ -68,6 +68,7 @@ suites_ascend = {
}
SUITES.update(suites_ascend)
STRICT_SUITES = {"unit"}
def parse_args():
@@ -288,13 +289,17 @@ def main():
for f_rel in suite_files_rel:
f_abs = target_dir / f_rel
if not f_abs.exists():
print(f"Warning: Test file {f_rel} not found in {target_dir}. Skipping.")
msg = f"Test file {f_rel} not found in {target_dir}."
if args.suite in STRICT_SUITES:
print(f"Error: {msg}")
sys.exit(1)
print(f"Warning: {msg} Skipping.")
continue
suite_files_abs.append(str(f_abs))
if not suite_files_abs:
print(f"No valid test files found for suite '{args.suite}'.")
sys.exit(0)
sys.exit(1 if args.suite in STRICT_SUITES else 0)
# 3. collect all test items and partition by items (not files)
all_test_items = collect_test_items(suite_files_abs, filter_expr=args.filter)

View File

@@ -3,22 +3,48 @@ import sys
import unittest
from unittest.mock import patch
from sglang.multimodal_gen.configs.pipeline_configs.base import PipelineConfig
from sglang.multimodal_gen.configs.pipeline_configs.qwen_image import (
QwenImagePipelineConfig,
)
from sglang.multimodal_gen.registry import _get_config_info
from sglang.multimodal_gen.runtime.server_args import ServerArgs
from sglang.multimodal_gen.utils import FlexibleArgumentParser
class TestServerArgsPathExpansion(unittest.TestCase):
def _from_dict_without_model_resolution(self, kwargs):
with patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
):
return ServerArgs.from_dict(kwargs)
def test_tilde_model_path_is_expanded(self):
args = ServerArgs.from_dict({"model_path": "~/fake/local/model"})
args = self._from_dict_without_model_resolution(
{"model_path": "~/fake/local/model"}
)
expected = os.path.expanduser("~/fake/local/model")
self.assertEqual(args.model_path, expected)
self.assertFalse(args.model_path.startswith("~"))
def test_absolute_path_is_unchanged(self):
args = ServerArgs.from_dict({"model_path": "/data/my-model"})
args = self._from_dict_without_model_resolution(
{"model_path": "/data/my-model"}
)
self.assertEqual(args.model_path, "/data/my-model")
def test_component_paths_are_expanded_before_pipeline_resolution(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_paths": {"vae": "~/fake/local/vae"},
}
)
self.assertEqual(
args.component_paths["vae"], os.path.expanduser("~/fake/local/vae")
)
class TestModelIdResolution(unittest.TestCase):
def setUp(self):
@@ -29,9 +55,6 @@ class TestModelIdResolution(unittest.TestCase):
# --model-id tells the engine which config to use
info = _get_config_info("/data/my-custom-qwen", model_id="Qwen-Image")
self.assertIsNotNone(info)
from sglang.multimodal_gen.configs.pipeline_configs.qwen_image import (
QwenImagePipelineConfig,
)
self.assertIs(info.pipeline_config_cls, QwenImagePipelineConfig)

View File

@@ -35,21 +35,25 @@ logger = init_logger(__name__)
T = TypeVar("T")
def _expand_path_value(field_name: str, value: Any) -> Any:
eu = os.path.expanduser
if field_name.endswith("_path") and isinstance(value, str):
return eu(value)
if field_name.endswith("_path") and isinstance(value, list):
return [eu(x) if isinstance(x, str) else x for x in value]
if field_name.endswith("_paths") and isinstance(value, dict):
return {k: eu(p) if isinstance(p, str) else p for k, p in value.items()}
return value
def expand_path_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
return {key: _expand_path_value(key, value) for key, value in kwargs.items()}
def expand_path_fields(obj) -> None:
"""In-place expanduser on all dataclass fields whose name ends with '_path' or '_paths'."""
eu = os.path.expanduser
for f in fields(obj):
v = getattr(obj, f.name)
if f.name.endswith("_path") and isinstance(v, str):
setattr(obj, f.name, eu(v))
elif f.name.endswith("_path") and isinstance(v, list):
setattr(obj, f.name, [eu(x) if isinstance(x, str) else x for x in v])
elif f.name.endswith("_paths") and isinstance(v, dict):
setattr(
obj,
f.name,
{k: eu(p) if isinstance(p, str) else p for k, p in v.items()},
)
setattr(obj, f.name, _expand_path_value(f.name, getattr(obj, f.name)))
# TODO(will): used to convert server_args.precision to torch.dtype. Find a