[Entrypoint][Refactor] vLLM-Omni Entrypoint Refactoring (#1908)

Signed-off-by: yinpe <11810305@mail.sustech.edu.cn>
Signed-off-by: Peiqi Yin <yinpeiqi809@gmail.com>
Signed-off-by: yinpeiqi <yinpeiqi809@gmail.com>
Signed-off-by: wuhang <whlbx@hotmail.com>
Signed-off-by: linyueqian <linyueqian@outlook.com>
Signed-off-by: princepride <wangzhipeng628@gmail.com>
Signed-off-by: lishunyang <lishunyang12@163.com>
Signed-off-by: Chenguang ZHENG <645327136@qq.com>
Signed-off-by: Peiqi Yin <60515999+yinpeiqi@users.noreply.github.com>
Co-authored-by: yinpe <11810305@mail.sustech.edu.cn>
Co-authored-by: Peiqi Yin <yinpeiqi809@gmail.com>
Co-authored-by: wuhang <whlbx@hotmail.com>
Co-authored-by: Peiqi Yin <60515999+yinpeiqi@users.noreply.github.com>
Co-authored-by: linyueqian <linyueqian@outlook.com>
Co-authored-by: princepride <wangzhipeng628@gmail.com>
Co-authored-by: lishunyang <lishunyang12@163.com>
This commit is contained in:
authored and GitHub committed 2026-03-18 17:33:53 +08:00
1 parent 6a66a1fe36
commit 3158e9d28f
125 files changed
+5210 -9322

No files matched your search

@@ -10,6 +10,7 @@ OpenAI-compatible async text-to-image generation API endpoints in api_server.py.
import base64
import io
from argparse import Namespace
from types import SimpleNamespace
import pytest
from fastapi.testclient import TestClient
@@ -115,7 +116,10 @@ class FakeAsyncOmni:
"""Fake AsyncOmni that yields a single diffusion output."""
def __init__(self):
self.stage_list = ["llm", "diffusion"]
self.stage_configs = [
SimpleNamespace(stage_type="llm"),
SimpleNamespace(stage_type="diffusion"),
]
self.default_sampling_params_list = [SamplingParams(temperature=0.1), OmniDiffusionSamplingParams()]
self.captured_sampling_params_list = None
self.captured_prompt = None
@@ -129,21 +133,25 @@ class FakeAsyncOmni:
@pytest.fixture
def mock_async_diffusion(mocker: MockerFixture):
"""Mock AsyncOmniDiffusion instance that returns fake images"""
mock = mocker.Mock()
mock.is_running = True # For health endpoint
mock.check_health = mocker.AsyncMock() # For LLM mode health check
"""Mock diffusion engine that matches the current async-generator API."""
async def generate(**kwargs):
# Return n PIL images wrapped in result object
n = kwargs["sampling_params_list"][0].num_outputs_per_prompt
mock.captured_sampling_params_list = kwargs["sampling_params_list"]
mock.captured_prompt = kwargs["prompt"]
images = [Image.new("RGB", (64, 64), color="blue") for _ in range(n)]
return MockGenerationResult(images)
class MockAsyncDiffusion:
def __init__(self) -> None:
self.is_running = True
self.check_health = mocker.AsyncMock()
self.captured_sampling_params_list = None
self.captured_prompt = None
self.generate_calls = 0
mock.generate = mocker.AsyncMock(side_effect=generate)
return mock
async def generate(self, **kwargs):
self.generate_calls += 1
n = kwargs["sampling_params_list"][0].num_outputs_per_prompt
self.captured_sampling_params_list = kwargs["sampling_params_list"]
self.captured_prompt = kwargs["prompt"]
images = [Image.new("RGB", (64, 64), color="blue") for _ in range(n)]
yield MockGenerationResult(images)
return MockAsyncDiffusion()
@pytest.fixture
@@ -159,7 +167,7 @@ def test_client(mock_async_diffusion):
# Set up app state with diffusion engine
app.state.engine_client = mock_async_diffusion
app.state.diffusion_engine = mock_async_diffusion # Also set for health endpoint
app.state.stage_configs = [{"stage_type": "diffusion"}]
app.state.stage_configs = [SimpleNamespace(stage_type="diffusion")]
app.state.diffusion_model_name = "Qwen/Qwen-Image" # For models endpoint
app.state.args = Namespace(
default_sampling_params='{"0": {"num_inference_steps":4, "guidance_scale":7.5}}',
@@ -180,7 +188,32 @@ def async_omni_test_client():
app.include_router(router)
app.state.engine_client = FakeAsyncOmni()
app.state.stage_configs = [{"stage_type": "llm"}, {"stage_type": "diffusion"}]
app.state.stage_configs = [
SimpleNamespace(stage_type="llm"),
SimpleNamespace(stage_type="diffusion"),
]
app.state.args = Namespace(
default_sampling_params='{"1": {"num_inference_steps":4, "guidance_scale":7.5}}',
max_generated_image_size=4096, # 64*64
)
return TestClient(app)
@pytest.fixture
def async_omni_stage_configs_only_client():
"""Create test client with refactored AsyncOmni compatibility surface only."""
from fastapi import FastAPI
from vllm_omni.entrypoints.openai.api_server import router
app = FastAPI()
app.include_router(router)
engine = FakeAsyncOmni()
assert not hasattr(engine, "stage_list")
app.state.engine_client = engine
# Intentionally do not populate app.state.stage_configs. Refactored
# AsyncOmni exposes stage_configs on the engine instance.
app.state.args = Namespace(
default_sampling_params='{"1": {"num_inference_steps":4, "guidance_scale":7.5}}',
max_generated_image_size=4096, # 64*64
@@ -291,6 +324,45 @@ def test_generate_images_async_omni_sampling_params(async_omni_test_client):
assert captured[1].seed == 7
def test_generate_images_async_omni_stage_configs_only(async_omni_stage_configs_only_client):
"""Regression: image generation accepts refactored AsyncOmni without stage_list."""
response = async_omni_stage_configs_only_client.post(
"/v1/images/generations",
json={
"prompt": "a castle",
"n": 1,
"size": "256x256",
"seed": 11,
},
)
assert response.status_code == 200
data = response.json()
assert len(data["data"]) == 1
engine = async_omni_stage_configs_only_client.app.state.engine_client
captured = engine.captured_sampling_params_list
assert captured is not None
assert len(captured) == 2
assert captured[1].seed == 11
def test_image_edits_async_omni_stage_configs_only(async_omni_stage_configs_only_client):
"""Regression: image edits accepts refactored AsyncOmni without stage_list."""
img_bytes = make_test_image_bytes((16, 16))
response = async_omni_stage_configs_only_client.post(
"/v1/images/edits",
files=[("image", img_bytes)],
data={
"prompt": "edit me",
"size": "auto",
},
)
assert response.status_code == 200
engine = async_omni_stage_configs_only_client.app.state.engine_client
captured = engine.captured_sampling_params_list
assert captured is not None
assert len(captured) == 2
def test_generate_multiple_images(test_client):
"""Test generating multiple images"""
response = test_client.post(
@@ -538,13 +610,12 @@ def test_parameters_passed_through(test_client, mock_async_diffusion):
)
assert response.status_code == 200
# Ensure generate() was called exactly once
mock_async_diffusion.generate.assert_awaited_once()
call_kwargs = mock_async_diffusion.generate.call_args[1]["sampling_params_list"][0]
assert call_kwargs.num_inference_steps == 100
assert call_kwargs.guidance_scale == 7.5
assert call_kwargs.true_cfg_scale == 3.0
assert call_kwargs.seed == 42
assert mock_async_diffusion.generate_calls == 1
captured = mock_async_diffusion.captured_sampling_params_list[0]
assert captured.num_inference_steps == 100
assert captured.guidance_scale == 7.5
assert captured.true_cfg_scale == 3.0
assert captured.seed == 42
def test_model_field_omitted_works(test_client):
@@ -63,9 +63,9 @@ def mock_engine_client(
default_other_params,
mocker: MockerFixture,
):
"""Create mock engine client with stage_list and default_sampling_params_list."""
"""Create mock engine client with stage_configs and default_sampling_params_list."""
engine_client = mocker.MagicMock()
engine_client.stage_list = [mock_comprehension_stage, mock_other_stage]
engine_client.stage_configs = [mock_comprehension_stage, mock_other_stage]
engine_client.default_sampling_params_list = [
default_comprehension_params,
default_other_params,
@@ -311,7 +311,7 @@ def test_get_comprehension_stage_index_finds_second_stage(mocker: MockerFixture)
comprehension.is_comprehension = True
instance.engine_client = mocker.MagicMock()
instance.engine_client.stage_list = [other, comprehension]
instance.engine_client.stage_configs = [other, comprehension]
assert instance._get_comprehension_stage_index() == 1
@@ -328,7 +328,7 @@ def test_get_comprehension_stage_index_raises_when_not_found(mocker: MockerFixtu
stage2.is_comprehension = False
instance.engine_client = mocker.MagicMock()
instance.engine_client.stage_list = [stage1, stage2]
instance.engine_client.stage_configs = [stage1, stage2]
with pytest.raises(ValueError, match="No comprehension stage"):
instance._get_comprehension_stage_index()
@@ -487,7 +487,7 @@ class TestTTSMethods:
def speech_server(self, mocker: MockerFixture):
mock_engine_client = mocker.MagicMock()
mock_engine_client.errored = False
mock_engine_client.stage_list = None
mock_engine_client.stage_configs = []
mock_engine_client.tts_max_instructions_length = None
mock_models = mocker.MagicMock()
mock_models.is_base_model.return_value = True
@@ -499,7 +499,7 @@ class TestTTSMethods:
def test_is_tts_detection_no_stage(self, speech_server):
"""Test TTS model detection when no TTS stage exists."""
# Fixture creates server with stage_list = None -> _is_tts should be False
# Fixture creates server with stage_configs = [] -> _is_tts should be False
assert speech_server._is_tts is False
assert speech_server._tts_stage is None
@@ -511,9 +511,9 @@ class TestTTSMethods:
# Create a TTS stage
mock_stage = mocker.MagicMock()
mock_stage.model_stage = "qwen3_tts"
mock_stage.engine_args.model_stage = "qwen3_tts"
mock_stage.tts_args = {}
mock_engine_client.stage_list = [mock_stage]
mock_engine_client.stage_configs = [mock_stage]
mock_models = mocker.MagicMock()
mock_models.is_base_model.return_value = True
@@ -614,7 +614,7 @@ class TestTTSMethods:
"""Test _load_supported_speakers."""
mock_engine_client = mocker.MagicMock()
mock_engine_client.errored = False
mock_engine_client.stage_list = None
mock_engine_client.stage_configs = []
# Mock talker_config with mixed-case speaker names
mock_talker_config = mocker.MagicMock()
@@ -757,7 +757,7 @@ class TestTTSMethods:
"""Test CLI override (stored in engine_client) takes highest priority."""
mock_engine_client = mocker.MagicMock()
mock_engine_client.errored = False
mock_engine_client.stage_list = None
mock_engine_client.stage_configs = []
# CLI override is stored in engine_client
mock_engine_client.tts_max_instructions_length = 1000
mock_models = mocker.MagicMock()
@@ -781,9 +781,9 @@ class TestTTSMethods:
# Mock stage with tts_args
mock_stage = mocker.MagicMock()
mock_stage.model_stage = "qwen3_tts"
mock_stage.engine_args.model_stage = "qwen3_tts"
mock_stage.tts_args = {"max_instructions_length": 750}
mock_engine_client.stage_list = [mock_stage]
mock_engine_client.stage_configs = [mock_stage]
server = OmniOpenAIServingSpeech(
engine_client=mock_engine_client,
@@ -804,9 +804,9 @@ class TestTTSMethods:
# Mock stage with tts_args
mock_stage = mocker.MagicMock()
mock_stage.model_stage = "qwen3_tts"
mock_stage.engine_args.model_stage = "qwen3_tts"
mock_stage.tts_args = {"max_instructions_length": 750}
mock_engine_client.stage_list = [mock_stage]
mock_engine_client.stage_configs = [mock_stage]
server = OmniOpenAIServingSpeech(
engine_client=mock_engine_client,
@@ -820,7 +820,7 @@ class TestTTSMethods:
"""Test instructions length validation uses cached _max_instructions_length."""
mock_engine_client = mocker.MagicMock()
mock_engine_client.errored = False
mock_engine_client.stage_list = None
mock_engine_client.stage_configs = []
# CLI override with max length of 10 characters
mock_engine_client.tts_max_instructions_length = 10
mock_models = mocker.MagicMock()
@@ -1034,15 +1034,12 @@ class TestAsyncOmniSupportedTasks:
@pytest.mark.asyncio
async def test_tts_only_no_generate_task(self):
"""TTS-only models (audio output, no text) should not include 'generate'."""
from unittest.mock import MagicMock
from types import SimpleNamespace
from vllm_omni.entrypoints.async_omni import AsyncOmni
omni = AsyncOmni.__new__(AsyncOmni)
omni.output_modalities = [None, "audio"]
stage = MagicMock()
stage.is_comprehension = False
omni.stage_list = [stage]
omni.engine = SimpleNamespace(supported_tasks=("speech",))
tasks = await omni.get_supported_tasks()
assert "generate" not in tasks
assert "speech" in tasks
@@ -1050,15 +1047,12 @@ class TestAsyncOmniSupportedTasks:
@pytest.mark.asyncio
async def test_omni_model_includes_generate(self):
"""Models with text output (e.g. Qwen3-Omni) should include 'generate'."""
from unittest.mock import MagicMock
from types import SimpleNamespace
from vllm_omni.entrypoints.async_omni import AsyncOmni
omni = AsyncOmni.__new__(AsyncOmni)
omni.output_modalities = ["text", None, "audio"]
stage = MagicMock()
stage.is_comprehension = True
omni.stage_list = [stage]
omni.engine = SimpleNamespace(supported_tasks=("generate", "speech"))
tasks = await omni.get_supported_tasks()
assert "generate" in tasks
@@ -44,7 +44,7 @@ class MockVideoResult:
class FakeAsyncOmni:
def __init__(self):
self.stage_list = ["diffusion"]
self.stage_configs = [SimpleNamespace(stage_type="diffusion")]
self.captured_prompt = None
self.captured_sampling_params_list = None
+65
View File
@@ -0,0 +1,65 @@
from types import SimpleNamespace
import pytest
from vllm.entrypoints.openai.models.protocol import BaseModelPath
from vllm.entrypoints.openai.models.serving import OpenAIServingModels
from vllm_omni.entrypoints.async_omni import AsyncOmni
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
@pytest.mark.asyncio
async def test_get_supported_tasks_returns_engine_supported_tasks():
omni = object.__new__(AsyncOmni)
omni.engine = SimpleNamespace(supported_tasks=("generate", "speech"))
supported_tasks = await omni.get_supported_tasks()
assert supported_tasks == ("generate", "speech")
def test_model_config_and_vllm_config_forward_from_comprehension_stage():
model_config = SimpleNamespace(model="Qwen/Qwen3-TTS")
vllm_config = SimpleNamespace(model_config=model_config)
renderer = SimpleNamespace(name="renderer")
input_processor = SimpleNamespace(renderer=renderer)
io_processor = SimpleNamespace(name="io-processor")
omni = object.__new__(AsyncOmni)
omni.engine = SimpleNamespace(
stage_clients=[SimpleNamespace(is_comprehension=False), SimpleNamespace(is_comprehension=True)],
stage_vllm_configs=[None, vllm_config],
)
omni.input_processor = input_processor
omni.io_processor = io_processor
assert omni.vllm_config is vllm_config
assert omni.model_config is model_config
assert omni.renderer is renderer
assert omni.input_processor is input_processor
assert omni.io_processor is io_processor
def test_openai_serving_models_can_consume_async_omni_compat_attrs():
model_config = SimpleNamespace(model="Qwen/Qwen3-TTS", max_model_len=32768)
vllm_config = SimpleNamespace(model_config=model_config)
renderer = SimpleNamespace(name="renderer")
input_processor = SimpleNamespace(renderer=renderer)
io_processor = SimpleNamespace(name="io-processor")
omni = object.__new__(AsyncOmni)
omni.engine = SimpleNamespace(
stage_clients=[SimpleNamespace(is_comprehension=True)],
stage_vllm_configs=[vllm_config],
)
omni.input_processor = input_processor
omni.io_processor = io_processor
serving_models = OpenAIServingModels(
engine_client=omni,
base_model_paths=[BaseModelPath(name="tts-model", model_path="Qwen/Qwen3-TTS")],
)
assert serving_models.model_config is model_config
assert serving_models.renderer is renderer
assert serving_models.io_processor is io_processor
assert serving_models.input_processor is input_processor
@@ -3,87 +3,51 @@
import pytest
from vllm_omni.entrypoints import utils as utils_module
from vllm_omni.entrypoints.async_omni import AsyncOmni
from vllm_omni.engine.async_omni_engine import AsyncOmniEngine
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
MODEL = "riverclouds/qwen_image_random"
def test_default_stage_config_includes_cache_backend():
"""Ensure cache knobs survive the default diffusion-stage builder."""
stage_cfg = AsyncOmniEngine._create_default_diffusion_stage_cfg(
{
"cache_backend": "cache_dit",
"cache_config": '{"Fn_compute_blocks": 2}',
"vae_use_slicing": True,
"ulysses_degree": 2,
}
)[0]
engine_args = stage_cfg["engine_args"]
assert stage_cfg["stage_type"] == "diffusion"
assert engine_args["cache_backend"] == "cache_dit"
assert engine_args["cache_config"]["Fn_compute_blocks"] == 2
assert engine_args["vae_use_slicing"] is True
assert engine_args["parallel_config"].ulysses_degree == 2
assert engine_args["model_stage"] == "diffusion"
def _noop_inline_engine(self, model, stage_config, kwargs):
self._inline_diffusion = False
self._inline_engine = None
def test_default_cache_config_used_when_missing():
"""Ensure default cache_config is synthesized when only backend is given."""
stage_cfg = AsyncOmniEngine._create_default_diffusion_stage_cfg(
{
"cache_backend": "cache_dit",
}
)[0]
def test_default_stage_config_includes_cache_backend(monkeypatch):
"""Ensure cache_backend/cache_config are preserved in default diffusion stage."""
monkeypatch.setattr(utils_module, "load_stage_configs_from_model", lambda model, base_engine_args=None: [])
monkeypatch.setattr(utils_module, "resolve_model_config_path", lambda model: None)
monkeypatch.setattr(AsyncOmni, "_start_stages", lambda self, model: None)
monkeypatch.setattr(AsyncOmni, "_wait_for_stages_ready", lambda self, timeout=0: None)
monkeypatch.setattr(AsyncOmni, "_init_inline_diffusion_engine", _noop_inline_engine)
omni = AsyncOmni(
model=MODEL,
cache_backend="cache_dit",
cache_config='{"Fn_compute_blocks": 2}',
vae_use_slicing=True,
ulysses_degree=2,
)
stage_cfg = omni.stage_configs[0]
engine_args = stage_cfg.engine_args
assert engine_args.get("cache_backend") == "cache_dit"
cache_config = engine_args.get("cache_config")
assert cache_config["Fn_compute_blocks"] == 2
assert engine_args.get("vae_use_slicing") is True
parallel_config = engine_args.get("parallel_config")
if hasattr(parallel_config, "get"):
ulysses_degree = parallel_config.get("ulysses_degree")
else:
ulysses_degree = getattr(parallel_config, "ulysses_degree", None)
assert ulysses_degree == 2
def test_default_cache_config_used_when_missing(monkeypatch):
"""Ensure default cache_config is applied when cache_backend is set."""
monkeypatch.setattr(utils_module, "load_stage_configs_from_model", lambda model, base_engine_args=None: [])
monkeypatch.setattr(utils_module, "resolve_model_config_path", lambda model: None)
monkeypatch.setattr(AsyncOmni, "_start_stages", lambda self, model: None)
monkeypatch.setattr(AsyncOmni, "_wait_for_stages_ready", lambda self, timeout=0: None)
monkeypatch.setattr(AsyncOmni, "_init_inline_diffusion_engine", _noop_inline_engine)
omni = AsyncOmni(
model=MODEL,
cache_backend="cache_dit",
)
engine_args = omni.stage_configs[0].engine_args
cache_config = engine_args.get("cache_config")
cache_config = stage_cfg["engine_args"]["cache_config"]
assert cache_config is not None
assert cache_config["Fn_compute_blocks"] == 1
def test_default_stage_devices_from_sequence_parallel(monkeypatch):
"""Ensure devices list reflects sequence parallel size when no parallel_config is provided."""
monkeypatch.setattr(utils_module, "load_stage_configs_from_model", lambda model, base_engine_args=None: [])
monkeypatch.setattr(utils_module, "resolve_model_config_path", lambda model: None)
monkeypatch.setattr(AsyncOmni, "_start_stages", lambda self, model: None)
monkeypatch.setattr(AsyncOmni, "_wait_for_stages_ready", lambda self, timeout=0: None)
monkeypatch.setattr(AsyncOmni, "_init_inline_diffusion_engine", _noop_inline_engine)
def test_default_stage_devices_from_sequence_parallel():
"""Ensure runtime devices reflect computed diffusion world size."""
stage_cfg = AsyncOmniEngine._create_default_diffusion_stage_cfg(
{
"ulysses_degree": 2,
"ring_degree": 2,
}
)[0]
omni = AsyncOmni(
model=MODEL,
ulysses_degree=2,
ring_degree=2,
)
stage_cfg = omni.stage_configs[0]
runtime = stage_cfg.runtime
if hasattr(runtime, "get"):
devices = runtime.get("devices")
else:
devices = getattr(runtime, "devices", None)
assert devices == "0,1,2,3"
assert stage_cfg["runtime"]["devices"] == "0,1,2,3"
File diff suppressed because it is too large. Load diff
@@ -1,63 +0,0 @@
import pytest
from vllm_omni.inputs.preprocess import OmniInputPreprocessor
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
def _make_preprocessor(monkeypatch):
preprocessor = object.__new__(OmniInputPreprocessor)
monkeypatch.setattr(preprocessor, "_truncate_inputs", lambda tokens, tokenization_kwargs=None: tokens)
monkeypatch.setattr(
preprocessor,
"_process_multimodal",
lambda *args, **kwargs: {"prompt_token_ids": [1, 2, 3]},
)
monkeypatch.setattr(preprocessor, "_tokenize_prompt", lambda prompt_text, tokenization_kwargs=None: [9, 8, 7])
return preprocessor
def test_process_tokens_keeps_additional_information(monkeypatch):
preprocessor = _make_preprocessor(monkeypatch)
parsed = {
"prompt_token_ids": [1, 2, 3],
"prompt_embeds": "embeds",
"additional_information": {"task": ["tts"], "lang": ["auto"]},
}
inputs = OmniInputPreprocessor._process_tokens(preprocessor, parsed)
assert inputs["prompt_token_ids"] == [1, 2, 3]
assert inputs["prompt_embeds"] == "embeds"
assert inputs["additional_information"] == {"task": ["tts"], "lang": ["auto"]}
def test_process_text_keeps_additional_information(monkeypatch):
preprocessor = _make_preprocessor(monkeypatch)
parsed = {
"prompt": "hello",
"prompt_embeds": "embeds",
"additional_information": {"speaker": ["alice"]},
}
inputs = OmniInputPreprocessor._process_text(preprocessor, parsed)
assert inputs["prompt_token_ids"] == [9, 8, 7]
assert inputs["prompt_embeds"] == "embeds"
assert inputs["additional_information"] == {"speaker": ["alice"]}
def test_process_text_multimodal_skips_empty_payloads(monkeypatch):
preprocessor = _make_preprocessor(monkeypatch)
parsed = {
"prompt": "hello",
"multi_modal_data": {"image": "fake"},
"prompt_embeds": None,
"additional_information": None,
}
inputs = OmniInputPreprocessor._process_text(preprocessor, parsed)
assert inputs["prompt_token_ids"] == [1, 2, 3]
assert "prompt_embeds" not in inputs
assert "additional_information" not in inputs
File diff suppressed because it is too large. Load diff
@@ -1,31 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest
from vllm_omni.entrypoints.omni_stage import _build_od_config
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
def test_build_od_config_includes_diffusion_fields():
engine_args = {
"cache_backend": "cache_dit",
"cache_config": {"Fn_compute_blocks": 2},
"vae_use_slicing": True,
}
od_config = _build_od_config(engine_args, model="dummy-model")
assert od_config["model"] == "dummy-model"
assert od_config["cache_backend"] == "cache_dit"
assert od_config["cache_config"]["Fn_compute_blocks"] == 2
assert od_config["vae_use_slicing"] is True
def test_build_od_config_respects_explicit_config():
engine_args = {
"od_config": {"cache_backend": "tea_cache"},
"cache_backend": "cache_dit",
}
od_config = _build_od_config(engine_args, model="dummy-model")
assert od_config == {"cache_backend": "tea_cache"}