2026-09-11 16:47:25 +08:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
2026-09-19 22:16:13 +04:00
|
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
2026-09-11 16:47:25 +08:00
|
|
|
"""Integration coverage for the shared task examples' ``_apply_ar_stage_inputs``.
|
|
|
|
|
|
|
|
|
|
The unit tests in ``test_model_extras.py`` cover ``get_ar_input_builder`` and
|
|
|
|
|
``build_ar_stage_inputs`` in isolation, but nothing previously exercised the
|
|
|
|
|
*glue* in ``text_to_image.py`` / ``image_edit.py`` that wires them together:
|
|
|
|
|
resolving the model's declared ``ar_input_builder`` via the registry, loading
|
|
|
|
|
the AR tokenizer, and writing the result onto ``prompt_dict`` /
|
|
|
|
|
``sampling_params_list``. A regression in that glue -- e.g. the registry key
|
|
|
|
|
mismatch fixed in this PR (``HunyuanImage3ForCausalMM`` vs
|
|
|
|
|
``HunyuanImage3Pipeline``), or the ``trust_remote_code`` opt-in being
|
|
|
|
|
silently bypassed -- would not have been caught by the isolated unit tests
|
|
|
|
|
above, since they call ``build_ar_stage_inputs`` directly rather than going
|
|
|
|
|
through the shared script's own code path.
|
|
|
|
|
|
|
|
|
|
These tests dynamically load the example scripts by file path (``examples/``
|
|
|
|
|
is not an installed package) and call ``_apply_ar_stage_inputs`` the same way
|
|
|
|
|
``main()`` does, with a fake tokenizer standing in for a real HF download.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import importlib.util
|
|
|
|
|
import sys
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from pathlib import Path
|
2026-09-19 22:16:13 +04:00
|
|
|
from types import SimpleNamespace
|
2026-09-11 16:47:25 +08:00
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
|
|
|
|
|
|
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
|
|
|
|
|
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
|
|
|
|
|
2026-09-19 22:16:13 +04:00
|
|
|
@pytest.mark.parametrize("with_ar_stage", [False, True])
|
|
|
|
|
def test_text_to_image_only_builds_ar_inputs_for_ar_deployments(monkeypatch, tmp_path, with_ar_stage):
|
|
|
|
|
from PIL import Image
|
|
|
|
|
from vllm import SamplingParams
|
|
|
|
|
|
|
|
|
|
from vllm_omni.config.omni_config import normalize_and_validate_diffusion_engine_ingress_kwargs
|
|
|
|
|
|
|
|
|
|
module = _load_module("_shared_text_to_image_deploy", "examples/offline_inference/text_to_image/text_to_image.py")
|
|
|
|
|
tokenizer_calls = _patch_tokenizer_loader(monkeypatch, module)
|
|
|
|
|
monkeypatch.setattr(module, "get_ar_tokenizer_validator", lambda _: None)
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
module, "current_omni_platform", SimpleNamespace(device_type="cpu", pre_register_and_update=lambda parser: None)
|
|
|
|
|
)
|
|
|
|
|
monkeypatch.setattr(module, "is_nextstep_model", lambda _: False)
|
|
|
|
|
monkeypatch.setattr(module, "get_model_class_name", lambda _: "HunyuanImage3ForCausalMM")
|
|
|
|
|
defaults = [OmniDiffusionSamplingParams()]
|
|
|
|
|
if with_ar_stage:
|
|
|
|
|
defaults.insert(0, SamplingParams())
|
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
|
|
|
|
|
|
def generate(prompt, *, sampling_params_list):
|
|
|
|
|
captured.update(prompt=prompt, params=sampling_params_list)
|
|
|
|
|
return [SimpleNamespace(images=[Image.new("RGB", (8, 8))])]
|
|
|
|
|
|
|
|
|
|
def create_omni(**kwargs):
|
|
|
|
|
# Exercise the real ingress validator so stale example-only options
|
|
|
|
|
# cannot silently pass this test while breaking model initialization.
|
|
|
|
|
normalize_and_validate_diffusion_engine_ingress_kwargs(kwargs, stage_id=0)
|
|
|
|
|
return SimpleNamespace(default_sampling_params_list=defaults, generate=generate)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(module, "Omni", create_omni)
|
|
|
|
|
output = tmp_path / "output.png"
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
sys,
|
|
|
|
|
"argv",
|
|
|
|
|
[
|
|
|
|
|
"text_to_image.py",
|
|
|
|
|
"--model",
|
|
|
|
|
"test-hunyuan",
|
|
|
|
|
"--prompt",
|
|
|
|
|
"A dog",
|
|
|
|
|
"--output",
|
|
|
|
|
str(output),
|
|
|
|
|
"--extra-body",
|
|
|
|
|
'{"bot_task": null}' if with_ar_stage else '{"bot_task": "none"}',
|
|
|
|
|
"--trust-remote-code",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
module.main()
|
|
|
|
|
|
|
|
|
|
assert output.is_file()
|
|
|
|
|
assert bool(tokenizer_calls) == with_ar_stage
|
|
|
|
|
assert ("prompt_token_ids" in captured["prompt"]) == with_ar_stage
|
|
|
|
|
if with_ar_stage:
|
|
|
|
|
assert captured["params"][0].stop_token_ids
|
|
|
|
|
else:
|
|
|
|
|
assert captured["prompt"] == {"prompt": "A dog", "modalities": ["image"]}
|
|
|
|
|
assert captured["params"][0].extra_args["bot_task"] == "none"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_hunyuan_accuracy_offline_cli_contract(monkeypatch, tmp_path):
|
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
accuracy = _load_module("_hunyuan_accuracy", "tests/e2e/accuracy/test_hunyuan_image3_pixel_accuracy.py")
|
|
|
|
|
module = _load_module("_hunyuan_accuracy_cli", "examples/offline_inference/text_to_image/text_to_image.py")
|
|
|
|
|
output = tmp_path / "offline.png"
|
|
|
|
|
|
|
|
|
|
def run(argv, *, check):
|
|
|
|
|
assert check
|
|
|
|
|
assert Path(argv[1]).is_file()
|
|
|
|
|
monkeypatch.setattr(sys, "argv", argv[1:])
|
|
|
|
|
args = module.parse_args()
|
|
|
|
|
assert args.enable_expert_parallel
|
|
|
|
|
assert args.extra_body == {"bot_task": "none"}
|
|
|
|
|
assert args.use_system_prompt == "en_unified"
|
|
|
|
|
assert args.trust_remote_code
|
|
|
|
|
assert args.seed == accuracy.SEED
|
|
|
|
|
assert args.num_inference_steps == accuracy.NUM_INFERENCE_STEPS
|
|
|
|
|
assert args.output == str(output)
|
|
|
|
|
Image.new("RGB", (8, 8)).save(args.output)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(subprocess, "run", run)
|
|
|
|
|
image = accuracy._run_vllm_omni_hunyuan_image3_offline(
|
|
|
|
|
model="test-hunyuan", deploy_config="deploy.yaml", output_path=output
|
|
|
|
|
)
|
|
|
|
|
assert image.size == (8, 8)
|
|
|
|
|
|
|
|
|
|
|
2026-09-11 16:47:25 +08:00
|
|
|
def _load_module(name: str, relpath: str):
|
|
|
|
|
path = _REPO_ROOT / relpath
|
|
|
|
|
spec = importlib.util.spec_from_file_location(name, path)
|
|
|
|
|
assert spec is not None and spec.loader is not None
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
sys.modules[name] = module
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
return module
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _FakeHunyuanTokenizer:
|
|
|
|
|
"""Mirrors test_model_extras.py's fixture: distinct ids, no network I/O."""
|
|
|
|
|
|
|
|
|
|
SPECIAL = {"<|startoftext|>": 1, "<img>": 2, "<think>": 3, "<recaption>": 4}
|
|
|
|
|
|
|
|
|
|
def convert_tokens_to_ids(self, tok: str) -> int:
|
|
|
|
|
return self.SPECIAL.get(tok, 0)
|
|
|
|
|
|
|
|
|
|
def encode(self, text: str, add_special_tokens: bool = False) -> list[int]:
|
|
|
|
|
return [500 + len(text)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class _FakeARSamplingParams:
|
|
|
|
|
"""Stands in for an LLM (non-diffusion) stage's sampling params."""
|
|
|
|
|
|
|
|
|
|
stop_token_ids: list[int] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _patch_tokenizer_loader(monkeypatch: pytest.MonkeyPatch, module: Any) -> list[dict[str, Any]]:
|
|
|
|
|
"""Patch the ``AutoTokenizer`` the module imports locally, recording calls."""
|
|
|
|
|
calls: list[dict[str, Any]] = []
|
|
|
|
|
|
|
|
|
|
class _FakeAutoTokenizer:
|
|
|
|
|
@staticmethod
|
|
|
|
|
def from_pretrained(model: str, trust_remote_code: bool = False) -> _FakeHunyuanTokenizer:
|
|
|
|
|
calls.append({"model": model, "trust_remote_code": trust_remote_code})
|
|
|
|
|
return _FakeHunyuanTokenizer()
|
|
|
|
|
|
2026-09-19 22:16:13 +04:00
|
|
|
monkeypatch.setattr("transformers.AutoTokenizer.from_pretrained", _FakeAutoTokenizer.from_pretrained)
|
2026-09-11 16:47:25 +08:00
|
|
|
return calls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _patch_failing_tokenizer_loader(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
|
|
"""Patch AutoTokenizer.from_pretrained to always raise, simulating a
|
|
|
|
|
missing --trust-remote-code / network / cache failure."""
|
|
|
|
|
|
|
|
|
|
class _FailingAutoTokenizer:
|
|
|
|
|
@staticmethod
|
|
|
|
|
def from_pretrained(model: str, trust_remote_code: bool = False) -> Any:
|
|
|
|
|
raise OSError("simulated tokenizer load failure")
|
|
|
|
|
|
2026-09-19 22:16:13 +04:00
|
|
|
monkeypatch.setattr("transformers.AutoTokenizer.from_pretrained", _FailingAutoTokenizer.from_pretrained)
|
2026-09-11 16:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image", "examples/offline_inference/text_to_image/text_to_image.py"),
|
|
|
|
|
("_shared_image_edit", "examples/offline_inference/image_to_image/image_edit.py"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_apply_ar_stage_inputs_routes_through_real_registry(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch, module_name: str, relpath: str
|
|
|
|
|
) -> None:
|
|
|
|
|
"""The shared script's glue must resolve HunyuanImage3's *real* registry
|
|
|
|
|
entry (keyed on the runtime ``model_class_name``) and write its output
|
|
|
|
|
onto the request -- not a stub, not a hardcoded id."""
|
|
|
|
|
from vllm_omni.model_extras import get_ar_input_builder
|
|
|
|
|
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
tokenizer_calls = _patch_tokenizer_loader(monkeypatch, module)
|
|
|
|
|
|
|
|
|
|
ar_input_builder = get_ar_input_builder("HunyuanImage3ForCausalMM")
|
|
|
|
|
assert ar_input_builder is not None, (
|
|
|
|
|
"get_ar_input_builder('HunyuanImage3ForCausalMM') returned None -- "
|
|
|
|
|
"if this regresses, the shared script silently skips AR-stage "
|
|
|
|
|
"input building entirely (this is exactly how the registry-key bug "
|
|
|
|
|
"this PR fixes went undetected)."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prompt_dict: dict[str, Any] = {}
|
|
|
|
|
ar_params = _FakeARSamplingParams()
|
|
|
|
|
diffusion_params = OmniDiffusionSamplingParams()
|
|
|
|
|
sampling_params_list: list[Any] = [ar_params, diffusion_params]
|
|
|
|
|
|
|
|
|
|
module._apply_ar_stage_inputs(
|
|
|
|
|
ar_input_builder,
|
|
|
|
|
model="tencent/HunyuanImage-3.0-Instruct",
|
|
|
|
|
prompt_text="a red panda",
|
|
|
|
|
extra_body={"bot_task": "think", "use_system_prompt": "en_recaption"},
|
|
|
|
|
num_images=0,
|
|
|
|
|
height=1024,
|
|
|
|
|
width=1024,
|
|
|
|
|
prompt_dict=prompt_dict,
|
|
|
|
|
sampling_params_list=sampling_params_list,
|
|
|
|
|
trust_remote_code=False,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# The tokenizer was loaded exactly once, for the model the user asked for.
|
|
|
|
|
assert len(tokenizer_calls) == 1
|
|
|
|
|
assert tokenizer_calls[0]["model"] == "tencent/HunyuanImage-3.0-Instruct"
|
|
|
|
|
|
|
|
|
|
# Byte-for-byte tokenizer path: prompt_token_ids populated, not the string form.
|
|
|
|
|
assert prompt_dict.get("prompt_token_ids"), "AR prefill token-ids were not written onto the request"
|
|
|
|
|
assert "prompt" not in prompt_dict
|
|
|
|
|
assert prompt_dict.get("use_system_prompt") == "en_recaption"
|
|
|
|
|
assert prompt_dict.get("modalities") == ["image"]
|
|
|
|
|
|
|
|
|
|
# stop_token_ids reached the AR-stage params, and only those. The diffusion
|
|
|
|
|
# stage must be left untouched: OmniDiffusionSamplingParams has no
|
|
|
|
|
# stop_token_ids field, so the helper skipping it means the attribute is
|
|
|
|
|
# never created (reading it would raise AttributeError, not return None).
|
|
|
|
|
assert ar_params.stop_token_ids, "AR stage did not receive stop_token_ids"
|
|
|
|
|
assert not hasattr(diffusion_params, "stop_token_ids"), "diffusion stage params must not receive stop_token_ids"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath,cli_flag",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image_trc", "examples/offline_inference/text_to_image/text_to_image.py", False),
|
|
|
|
|
("_shared_text_to_image_trc", "examples/offline_inference/text_to_image/text_to_image.py", True),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_apply_ar_stage_inputs_respects_trust_remote_code_option(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch, module_name: str, relpath: str, cli_flag: bool
|
|
|
|
|
) -> None:
|
|
|
|
|
"""The tokenizer load must use the caller's resolved --trust-remote-code
|
|
|
|
|
value, not silently default to True regardless of user intent."""
|
|
|
|
|
from vllm_omni.model_extras import get_ar_input_builder
|
|
|
|
|
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
tokenizer_calls = _patch_tokenizer_loader(monkeypatch, module)
|
|
|
|
|
ar_input_builder = get_ar_input_builder("HunyuanImage3ForCausalMM")
|
|
|
|
|
|
|
|
|
|
module._apply_ar_stage_inputs(
|
|
|
|
|
ar_input_builder,
|
|
|
|
|
model="tencent/HunyuanImage-3.0-Instruct",
|
|
|
|
|
prompt_text="a red panda",
|
|
|
|
|
extra_body={},
|
|
|
|
|
num_images=0,
|
|
|
|
|
height=1024,
|
|
|
|
|
width=1024,
|
|
|
|
|
prompt_dict={},
|
|
|
|
|
sampling_params_list=[_FakeARSamplingParams(), OmniDiffusionSamplingParams()],
|
|
|
|
|
trust_remote_code=cli_flag,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert tokenizer_calls[0]["trust_remote_code"] is cli_flag
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image_validate", "examples/offline_inference/text_to_image/text_to_image.py"),
|
|
|
|
|
("_shared_image_edit_validate", "examples/offline_inference/image_to_image/image_edit.py"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_apply_ar_stage_inputs_runs_declared_tokenizer_validator(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch, module_name: str, relpath: str
|
|
|
|
|
) -> None:
|
|
|
|
|
"""get_ar_tokenizer_validator's hook must run on a real tokenizer load,
|
|
|
|
|
and a validation failure must propagate -- not get swallowed by the
|
|
|
|
|
tokenizer-load try/except (which would silently fall back to the string
|
|
|
|
|
prompt instead of surfacing the drift)."""
|
|
|
|
|
from vllm_omni.model_extras import get_ar_input_builder, get_ar_tokenizer_validator
|
|
|
|
|
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
_patch_tokenizer_loader(monkeypatch, module)
|
|
|
|
|
ar_input_builder = get_ar_input_builder("HunyuanImage3ForCausalMM")
|
|
|
|
|
validator = get_ar_tokenizer_validator("HunyuanImage3ForCausalMM")
|
|
|
|
|
assert validator is not None
|
|
|
|
|
|
|
|
|
|
# The fake tokenizer's ids intentionally don't match
|
|
|
|
|
# HUNYUAN_IMAGE3_SPECIAL_TOKEN_IDS, so the real validator must reject it.
|
|
|
|
|
with pytest.raises(ValueError, match="no longer match"):
|
|
|
|
|
module._apply_ar_stage_inputs(
|
|
|
|
|
ar_input_builder,
|
|
|
|
|
model="tencent/HunyuanImage-3.0-Instruct",
|
|
|
|
|
prompt_text="a red panda",
|
|
|
|
|
extra_body={},
|
|
|
|
|
num_images=0,
|
|
|
|
|
height=1024,
|
|
|
|
|
width=1024,
|
|
|
|
|
prompt_dict={},
|
|
|
|
|
sampling_params_list=[_FakeARSamplingParams(), OmniDiffusionSamplingParams()],
|
|
|
|
|
trust_remote_code=False,
|
|
|
|
|
validate_tokenizer=validator,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath,required_argv",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image_cli", "examples/offline_inference/text_to_image/text_to_image.py", []),
|
|
|
|
|
# image_edit.py's parser declares --image/--prompt as required (no defaults);
|
|
|
|
|
# omitting them makes parse_args() raise SystemExit(2) before the assertion
|
|
|
|
|
# below is ever reached.
|
|
|
|
|
(
|
|
|
|
|
"_shared_image_edit_cli",
|
|
|
|
|
"examples/offline_inference/image_to_image/image_edit.py",
|
|
|
|
|
["--image", "dummy.png", "--prompt", "x"],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
@pytest.mark.parametrize("argv,expected", [([], False), (["--trust-remote-code"], True)])
|
|
|
|
|
def test_trust_remote_code_flag_parses_from_real_cli(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
|
|
module_name: str,
|
|
|
|
|
relpath: str,
|
|
|
|
|
required_argv: list[str],
|
|
|
|
|
argv: list[str],
|
|
|
|
|
expected: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""The other tests in this file call _apply_ar_stage_inputs directly with
|
|
|
|
|
an already-resolved trust_remote_code bool, which never exercises
|
|
|
|
|
argparse itself. Closing that specific gap doesn't need a GPU or a model
|
|
|
|
|
load -- parse_args() only touches sys.argv -- so cover it here instead of
|
|
|
|
|
the (expensive, one-model-load-per-invocation) full CLI subprocess a
|
|
|
|
|
stricter GPU e2e test would need."""
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
monkeypatch.setattr(sys, "argv", ["prog", *required_argv, *argv])
|
|
|
|
|
args = module.parse_args()
|
|
|
|
|
assert args.trust_remote_code is expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image_failfast", "examples/offline_inference/text_to_image/text_to_image.py"),
|
|
|
|
|
("_shared_image_edit_failfast", "examples/offline_inference/image_to_image/image_edit.py"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_apply_ar_stage_inputs_tokenizer_load_failure_raises_by_default(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch, module_name: str, relpath: str
|
|
|
|
|
) -> None:
|
|
|
|
|
"""A tokenizer load failure must surface as an error by default, not
|
|
|
|
|
silently degrade to the string-prompt form -- that degraded form can
|
|
|
|
|
complete "successfully" with subtly wrong stop tokens, masking the real
|
|
|
|
|
problem (e.g. a missing --trust-remote-code)."""
|
|
|
|
|
from vllm_omni.model_extras import get_ar_input_builder
|
|
|
|
|
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
_patch_failing_tokenizer_loader(monkeypatch)
|
|
|
|
|
ar_input_builder = get_ar_input_builder("HunyuanImage3ForCausalMM")
|
|
|
|
|
|
|
|
|
|
with pytest.raises(OSError, match="simulated tokenizer load failure"):
|
|
|
|
|
module._apply_ar_stage_inputs(
|
|
|
|
|
ar_input_builder,
|
|
|
|
|
model="tencent/HunyuanImage-3.0-Instruct",
|
|
|
|
|
prompt_text="a red panda",
|
|
|
|
|
extra_body={},
|
|
|
|
|
num_images=0,
|
|
|
|
|
height=1024,
|
|
|
|
|
width=1024,
|
|
|
|
|
prompt_dict={},
|
|
|
|
|
sampling_params_list=[_FakeARSamplingParams(), OmniDiffusionSamplingParams()],
|
|
|
|
|
trust_remote_code=False,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"module_name,relpath",
|
|
|
|
|
[
|
|
|
|
|
("_shared_text_to_image_fallback", "examples/offline_inference/text_to_image/text_to_image.py"),
|
|
|
|
|
("_shared_image_edit_fallback", "examples/offline_inference/image_to_image/image_edit.py"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_apply_ar_stage_inputs_allow_tokenizer_fallback_option(
|
|
|
|
|
monkeypatch: pytest.MonkeyPatch, module_name: str, relpath: str
|
|
|
|
|
) -> None:
|
|
|
|
|
"""With allow_tokenizer_fallback=True (the CLI's --allow-tokenizer-fallback),
|
|
|
|
|
a tokenizer load failure degrades to the string-prompt form instead of
|
|
|
|
|
raising -- for a deliberate offline-compat run, not the default."""
|
|
|
|
|
from vllm_omni.model_extras import get_ar_input_builder
|
|
|
|
|
|
|
|
|
|
module = _load_module(module_name, relpath)
|
|
|
|
|
_patch_failing_tokenizer_loader(monkeypatch)
|
|
|
|
|
ar_input_builder = get_ar_input_builder("HunyuanImage3ForCausalMM")
|
|
|
|
|
|
|
|
|
|
prompt_dict: dict[str, Any] = {}
|
|
|
|
|
module._apply_ar_stage_inputs(
|
|
|
|
|
ar_input_builder,
|
|
|
|
|
model="tencent/HunyuanImage-3.0-Instruct",
|
|
|
|
|
prompt_text="a red panda",
|
|
|
|
|
extra_body={"bot_task": "recaption"},
|
|
|
|
|
num_images=0,
|
|
|
|
|
height=1024,
|
|
|
|
|
width=1024,
|
|
|
|
|
prompt_dict=prompt_dict,
|
|
|
|
|
sampling_params_list=[_FakeARSamplingParams(), OmniDiffusionSamplingParams()],
|
|
|
|
|
trust_remote_code=False,
|
|
|
|
|
allow_tokenizer_fallback=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# No tokenizer -> string-prompt form, not the byte-for-byte token-ids form.
|
|
|
|
|
assert "prompt_token_ids" not in prompt_dict
|
|
|
|
|
assert prompt_dict.get("prompt") and "a red panda" in prompt_dict["prompt"]
|