Merge branch 'main' into codex/unskip-fp8-z-image-quality
This commit is contained in:
56 files changed
+1821
-736
No files matched your search
@@ -60,6 +60,7 @@ def _make_request(**overrides) -> OmniDiffusionRequest:
|
||||
output_type="pil",
|
||||
generator_device="cpu",
|
||||
),
|
||||
"request_id": "diffusers-backend",
|
||||
}
|
||||
defaults.update(overrides)
|
||||
return OmniDiffusionRequest(**defaults)
|
||||
|
||||
@@ -48,6 +48,7 @@ def _make_request(prompts=None, **sp_kwargs) -> OmniDiffusionRequest:
|
||||
return OmniDiffusionRequest(
|
||||
prompts=prompts or [{"prompt": "a cat dancing"}],
|
||||
sampling_params=sp,
|
||||
request_id="dmd2-sanitize",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ def _make_pipeline(cls):
|
||||
|
||||
def _make_request(**sp_kwargs) -> OmniDiffusionRequest:
|
||||
sp = OmniDiffusionSamplingParams(**sp_kwargs)
|
||||
return OmniDiffusionRequest(prompts=[{"prompt": "a cat"}], sampling_params=sp)
|
||||
return OmniDiffusionRequest(prompts=[{"prompt": "a cat"}], sampling_params=sp, request_id="dmd2-scheduler")
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
|
||||
@@ -73,7 +73,7 @@ def test_should_apply_pe_respects_sampling_extra_args():
|
||||
|
||||
|
||||
def test_should_apply_pe_disables_dummy_warmup_request():
|
||||
req = SimpleNamespace(request_ids=["dummy_req_id"], sampling_params=SimpleNamespace(extra_args={}))
|
||||
req = SimpleNamespace(is_dummy_run=lambda: True, sampling_params=SimpleNamespace(extra_args={}))
|
||||
|
||||
assert ErnieImagePipeline._should_apply_pe(req) is False
|
||||
|
||||
|
||||
@@ -0,0 +1,741 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Unit tests for GLM-Image quantization support (W4A16/AutoRound).
|
||||
|
||||
These tests verify that the GLM-Image DiT transformer correctly accepts and uses
|
||||
quantization configs for W4A16/AutoRound quantization support.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from vllm_omni.diffusion.data import DiffusionParallelConfig
|
||||
from vllm_omni.diffusion.models.glm_image.glm_image_transformer import (
|
||||
ColumnParallelGELU,
|
||||
ColumnParallelSiLU,
|
||||
GlmImageAdaLayerNormContinuous,
|
||||
GlmImageAdaLayerNormZero,
|
||||
GlmImageAttention,
|
||||
GlmImageFeedForward,
|
||||
GlmImageImageProjector,
|
||||
GlmImagePrepare,
|
||||
GlmImageRotaryPosEmbed,
|
||||
GlmImageTransformer2DModel,
|
||||
GlmImageTransformerBlock,
|
||||
_positive_divisors,
|
||||
validate_glm_image_tp_constraints,
|
||||
)
|
||||
from vllm_omni.model_executor.models.glm_image.pipeline import GLM_IMAGE_PIPELINE
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def _init_distributed():
|
||||
"""Initialize the minimal distributed environment required by
|
||||
ReplicatedLinear (tensor-parallel group must exist)."""
|
||||
from vllm.distributed.parallel_state import (
|
||||
cleanup_dist_env_and_memory,
|
||||
init_distributed_environment,
|
||||
initialize_model_parallel,
|
||||
)
|
||||
|
||||
os.environ.setdefault("MASTER_ADDR", "localhost")
|
||||
os.environ.setdefault("MASTER_PORT", "29502")
|
||||
init_distributed_environment(
|
||||
world_size=1,
|
||||
rank=0,
|
||||
local_rank=0,
|
||||
distributed_init_method="env://",
|
||||
)
|
||||
initialize_model_parallel()
|
||||
yield
|
||||
cleanup_dist_env_and_memory()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def _force_default_gemm(monkeypatch):
|
||||
"""Force CPU-compatible GEMM dispatch for tests using CPU tensors.
|
||||
|
||||
vLLM's dispatch_unquantized_gemm() selects the backend by platform
|
||||
(e.g. rocm_unquantized_gemm on AMD machines), not by tensor device.
|
||||
CPU test tensors crash with NotImplementedError on ROCm. Monkeypatch
|
||||
the dispatcher to always return the default (torch.nn.functional.linear)
|
||||
implementation which works on any device."""
|
||||
from vllm.model_executor.layers.utils import default_unquantized_gemm
|
||||
|
||||
monkeypatch.setattr(
|
||||
"vllm.model_executor.layers.linear.dispatch_unquantized_gemm",
|
||||
lambda: default_unquantized_gemm,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def setup_mocks(mocker: MockerFixture):
|
||||
"""Set up common mocks for all tests."""
|
||||
mocker.patch(
|
||||
"vllm.model_executor.layers.linear.get_tensor_model_parallel_world_size",
|
||||
return_value=1,
|
||||
)
|
||||
mock_get_tp_group = mocker.patch("vllm.distributed.parallel_state.get_tp_group")
|
||||
mock_tp_group = mocker.MagicMock()
|
||||
mock_tp_group.world_size = 1
|
||||
mock_get_tp_group.return_value = mock_tp_group
|
||||
yield
|
||||
|
||||
|
||||
class TestPositiveDivisors:
|
||||
"""Test _positive_divisors helper function."""
|
||||
|
||||
def test_divisors_of_1(self):
|
||||
assert _positive_divisors(1) == {1}
|
||||
|
||||
def test_divisors_of_12(self):
|
||||
assert _positive_divisors(12) == {1, 2, 3, 4, 6, 12}
|
||||
|
||||
def test_divisors_of_prime(self):
|
||||
assert _positive_divisors(7) == {1, 7}
|
||||
|
||||
def test_divisors_of_0_returns_empty(self):
|
||||
assert _positive_divisors(0) == set()
|
||||
|
||||
def test_divisors_of_negative_returns_empty(self):
|
||||
assert _positive_divisors(-5) == set()
|
||||
|
||||
|
||||
class TestValidateGlmImageTpConstraints:
|
||||
"""Test TP constraint validation for GLM-Image."""
|
||||
|
||||
def test_valid_tp_size_1(self):
|
||||
"""TP=1 should always be valid."""
|
||||
result = validate_glm_image_tp_constraints(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=1,
|
||||
)
|
||||
assert 1 in result
|
||||
|
||||
def test_valid_tp_size_2_for_divisible_dim(self):
|
||||
"""TP=2 is valid when all dims are divisible by 2."""
|
||||
result = validate_glm_image_tp_constraints(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=2,
|
||||
)
|
||||
assert 1 in result
|
||||
assert 2 in result
|
||||
|
||||
def test_valid_tp_size_4_for_divisible_dim(self):
|
||||
"""TP=4 is valid when all dims are divisible by 4."""
|
||||
result = validate_glm_image_tp_constraints(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=4,
|
||||
)
|
||||
assert 1 in result
|
||||
assert 2 in result
|
||||
assert 4 in result
|
||||
|
||||
def test_invalid_tp_size_3_for_divisible_dim(self):
|
||||
"""TP=3 is invalid when dim is not divisible by 3."""
|
||||
with pytest.raises(ValueError, match="dim % tensor_parallel_size == 0"):
|
||||
validate_glm_image_tp_constraints(
|
||||
dim=2560, # 2560 % 3 != 0
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=3,
|
||||
)
|
||||
|
||||
def test_invalid_tp_size_zero(self):
|
||||
"""TP=0 should raise error."""
|
||||
with pytest.raises(ValueError, match="tensor_parallel_size must be > 0"):
|
||||
validate_glm_image_tp_constraints(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=0,
|
||||
)
|
||||
|
||||
def test_invalid_tp_size_negative(self):
|
||||
"""Negative TP size should raise error."""
|
||||
with pytest.raises(ValueError, match="tensor_parallel_size must be > 0"):
|
||||
validate_glm_image_tp_constraints(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
ffn_hidden_dim=10240,
|
||||
tensor_parallel_size=-1,
|
||||
)
|
||||
|
||||
|
||||
class TestGlmImageAdaLayerNormZeroQuantization:
|
||||
"""Test GlmImageAdaLayerNormZero with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify the class accepts quant_config parameter."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
layer = GlmImageAdaLayerNormZero(
|
||||
embedding_dim=512,
|
||||
dim=2560,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.norm1",
|
||||
)
|
||||
assert layer.linear.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
layer = GlmImageAdaLayerNormZero(
|
||||
embedding_dim=512,
|
||||
dim=2560,
|
||||
quant_config=None,
|
||||
)
|
||||
assert layer.linear.quant_config is None
|
||||
|
||||
def test_forward_handles_tuple_return_from_linear(self):
|
||||
"""Verify forward handles tuple returns from ReplicatedLinear."""
|
||||
layer = GlmImageAdaLayerNormZero(
|
||||
embedding_dim=512,
|
||||
dim=2560,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
batch_size = 2
|
||||
seq_len = 10
|
||||
hidden_states = torch.randn(batch_size, seq_len, 2560)
|
||||
encoder_hidden_states = torch.randn(batch_size, seq_len, 2560)
|
||||
temb = torch.randn(batch_size, 512)
|
||||
|
||||
# This should work regardless of whether linear returns tuple or tensor
|
||||
result = layer(hidden_states, encoder_hidden_states, temb)
|
||||
assert len(result) == 10 # Should return 10 chunks
|
||||
|
||||
|
||||
class TestGlmImageAdaLayerNormContinuousQuantization:
|
||||
"""Test GlmImageAdaLayerNormContinuous with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify the class accepts quant_config parameter."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
layer = GlmImageAdaLayerNormContinuous(
|
||||
embedding_dim=2560,
|
||||
conditioning_embedding_dim=512,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.norm_out",
|
||||
)
|
||||
assert layer.linear.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
layer = GlmImageAdaLayerNormContinuous(
|
||||
embedding_dim=2560,
|
||||
conditioning_embedding_dim=512,
|
||||
quant_config=None,
|
||||
)
|
||||
assert layer.linear.quant_config is None
|
||||
|
||||
def test_forward_handles_tuple_return_from_linear(self):
|
||||
"""Verify forward handles tuple returns from ReplicatedLinear."""
|
||||
layer = GlmImageAdaLayerNormContinuous(
|
||||
embedding_dim=2560,
|
||||
conditioning_embedding_dim=512,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
batch_size = 2
|
||||
seq_len = 10
|
||||
x = torch.randn(batch_size, seq_len, 2560)
|
||||
conditioning_embedding = torch.randn(batch_size, 512)
|
||||
|
||||
result = layer(x, conditioning_embedding)
|
||||
assert result.shape == (batch_size, seq_len, 2560)
|
||||
|
||||
|
||||
class TestGlmImageAttentionQuantization:
|
||||
"""Test GlmImageAttention with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify GlmImageAttention accepts quant_config parameter."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
attn = GlmImageAttention(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
head_dim=40,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.attn1",
|
||||
)
|
||||
assert attn.to_qkv.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
attn = GlmImageAttention(
|
||||
dim=2560,
|
||||
num_heads=64,
|
||||
head_dim=40,
|
||||
quant_config=None,
|
||||
)
|
||||
assert attn.to_qkv.quant_config is None
|
||||
|
||||
|
||||
class TestColumnParallelModulesQuantization:
|
||||
"""Test ColumnParallelGELU and ColumnParallelSiLU with quantization config."""
|
||||
|
||||
def test_column_parallel_gelu_accepts_quant_config(self, mocker: MockerFixture):
|
||||
"""Verify ColumnParallelGELU accepts quant_config."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
layer = ColumnParallelGELU(
|
||||
dim_in=2560,
|
||||
dim_out=10240,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.gelu",
|
||||
)
|
||||
assert layer.proj.quant_config is mock_quant_config
|
||||
|
||||
def test_column_parallel_silu_accepts_quant_config(self, mocker: MockerFixture):
|
||||
"""Verify ColumnParallelSiLU accepts quant_config."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
layer = ColumnParallelSiLU(
|
||||
dim_in=2560,
|
||||
dim_out=10240,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.silu",
|
||||
)
|
||||
assert layer.proj.quant_config is mock_quant_config
|
||||
|
||||
|
||||
class TestGlmImageFeedForwardQuantization:
|
||||
"""Test GlmImageFeedForward with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify GlmImageFeedForward accepts quant_config parameter."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
ff = GlmImageFeedForward(
|
||||
dim=2560,
|
||||
dim_out=2560,
|
||||
inner_dim=10240,
|
||||
activation_fn="gelu-approximate",
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.ff",
|
||||
)
|
||||
# Check that the first layer (ColumnParallelGELU) has quant_config
|
||||
gelu_layer = ff.net[0]
|
||||
assert gelu_layer.proj.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
ff = GlmImageFeedForward(
|
||||
dim=2560,
|
||||
dim_out=2560,
|
||||
inner_dim=10240,
|
||||
activation_fn="gelu-approximate",
|
||||
quant_config=None,
|
||||
)
|
||||
gelu_layer = ff.net[0]
|
||||
assert gelu_layer.proj.quant_config is None
|
||||
|
||||
def test_linear_silu_activation(self):
|
||||
"""Test linear-silu activation function initialization works."""
|
||||
# This test verifies the module can be instantiated with linear-silu activation.
|
||||
# Full forward testing requires proper TP group setup, so we just verify construction.
|
||||
ff = GlmImageFeedForward(
|
||||
dim=2560,
|
||||
dim_out=2560,
|
||||
inner_dim=10240,
|
||||
activation_fn="linear-silu",
|
||||
quant_config=None,
|
||||
)
|
||||
# Verify the FFN has the correct structure
|
||||
assert len(ff.net) == 3 # ColumnParallelSiLU, Identity, RowParallelLinear
|
||||
assert isinstance(ff.net[0], ColumnParallelSiLU)
|
||||
|
||||
|
||||
class TestGlmImageTransformerBlockQuantization:
|
||||
"""Test GlmImageTransformerBlock with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify GlmImageTransformerBlock accepts quant_config parameter."""
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
block = GlmImageTransformerBlock(
|
||||
dim=2560,
|
||||
num_attention_heads=64,
|
||||
attention_head_dim=40,
|
||||
time_embed_dim=512,
|
||||
parallel_config=parallel_config,
|
||||
quant_config=mock_quant_config,
|
||||
prefix="test.block",
|
||||
)
|
||||
# Check that inner modules have quant_config
|
||||
assert block.norm1.linear.quant_config is mock_quant_config
|
||||
assert block.attn1.to_qkv.quant_config is mock_quant_config
|
||||
assert block.ff.net[0].proj.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
block = GlmImageTransformerBlock(
|
||||
dim=2560,
|
||||
num_attention_heads=64,
|
||||
attention_head_dim=40,
|
||||
time_embed_dim=512,
|
||||
quant_config=None,
|
||||
)
|
||||
assert block.norm1.linear.quant_config is None
|
||||
|
||||
|
||||
class TestGlmImagePrepareModule:
|
||||
"""Test GlmImagePrepare module."""
|
||||
|
||||
def test_prepare_module_exists(self):
|
||||
"""Verify GlmImagePrepare module exists and works."""
|
||||
projector = GlmImageImageProjector(in_channels=16, hidden_size=2560, patch_size=2)
|
||||
rope = GlmImageRotaryPosEmbed(dim=40, patch_size=2)
|
||||
prepare = GlmImagePrepare(
|
||||
image_projector=projector,
|
||||
rope=rope,
|
||||
patch_size=2,
|
||||
)
|
||||
|
||||
# Test forward pass
|
||||
hidden_states = torch.randn(1, 16, 64, 64) # [B, C, H, W]
|
||||
result = prepare(hidden_states)
|
||||
|
||||
assert len(result) == 5
|
||||
hidden_out, rope_cos, rope_sin, height, width = result
|
||||
assert hidden_out.shape[0] == 1 # batch size
|
||||
assert hidden_out.shape[1] == 1024 # seq_len (32 * 32)
|
||||
assert hidden_out.shape[2] == 2560 # hidden_dim
|
||||
|
||||
|
||||
class TestGlmImagePipelineConfig:
|
||||
"""Test GLM_IMAGE_PIPELINE configuration."""
|
||||
|
||||
def test_glm_image_pipeline_config_exists(self):
|
||||
"""Verify GLM_IMAGE_PIPELINE config exists."""
|
||||
assert GLM_IMAGE_PIPELINE is not None
|
||||
|
||||
def test_pipeline_has_correct_model_type(self):
|
||||
"""Verify pipeline has correct model_type."""
|
||||
assert GLM_IMAGE_PIPELINE.model_type == "glm_image"
|
||||
|
||||
def test_pipeline_has_correct_model_arch(self):
|
||||
"""Verify pipeline has correct model_arch."""
|
||||
assert GLM_IMAGE_PIPELINE.model_arch == "GlmImageForConditionalGeneration"
|
||||
|
||||
def test_pipeline_has_two_stages(self):
|
||||
"""Verify pipeline has two stages (AR + DiT)."""
|
||||
assert len(GLM_IMAGE_PIPELINE.stages) == 2
|
||||
|
||||
def test_stage_0_is_ar_llm(self):
|
||||
"""Verify stage 0 is the AR LLM stage."""
|
||||
stage_0 = GLM_IMAGE_PIPELINE.stages[0]
|
||||
assert stage_0.stage_id == 0
|
||||
assert stage_0.model_stage == "ar"
|
||||
assert stage_0.execution_type.value == "llm_ar"
|
||||
assert stage_0.owns_tokenizer is True
|
||||
|
||||
def test_stage_1_is_diffusion(self):
|
||||
"""Verify stage 1 is the DiT diffusion stage."""
|
||||
stage_1 = GLM_IMAGE_PIPELINE.stages[1]
|
||||
assert stage_1.stage_id == 1
|
||||
assert stage_1.model_stage == "dit"
|
||||
assert stage_1.execution_type.value == "diffusion"
|
||||
assert stage_1.final_output is True
|
||||
assert stage_1.final_output_type == "image"
|
||||
assert stage_1.input_sources == (0,) # Takes input from stage 0
|
||||
|
||||
def test_pipeline_has_diffusers_class_name(self):
|
||||
"""Verify pipeline has diffusers_class_name."""
|
||||
assert GLM_IMAGE_PIPELINE.diffusers_class_name == "GlmImagePipeline"
|
||||
|
||||
|
||||
class TestGlmImageImageProjector:
|
||||
"""Test GlmImageImageProjector module."""
|
||||
|
||||
def test_projector_output_shape(self):
|
||||
"""Verify projector produces correct output shape."""
|
||||
projector = GlmImageImageProjector(
|
||||
in_channels=16,
|
||||
hidden_size=2560,
|
||||
patch_size=2,
|
||||
)
|
||||
|
||||
# Input: [B, C, H, W] = [1, 16, 64, 64]
|
||||
hidden_states = torch.randn(1, 16, 64, 64)
|
||||
output = projector(hidden_states)
|
||||
|
||||
# After patchify: [B, H/2*W/2, D] = [1, 32*32, 2560] = [1, 1024, 2560]
|
||||
assert output.shape == (1, 1024, 2560)
|
||||
|
||||
def test_projector_with_different_patch_sizes(self):
|
||||
"""Verify projector works with different patch sizes."""
|
||||
for patch_size in [2, 4]:
|
||||
projector = GlmImageImageProjector(
|
||||
in_channels=16,
|
||||
hidden_size=2560,
|
||||
patch_size=patch_size,
|
||||
)
|
||||
h, w = 64, 64
|
||||
hidden_states = torch.randn(1, 16, h, w)
|
||||
output = projector(hidden_states)
|
||||
expected_seq_len = (h // patch_size) * (w // patch_size)
|
||||
assert output.shape[1] == expected_seq_len
|
||||
|
||||
|
||||
class TestGlmImageRotaryPosEmbed:
|
||||
"""Test GlmImageRotaryPosEmbed module."""
|
||||
|
||||
def test_rope_output_shape(self):
|
||||
"""Verify RoPE produces correct output shape."""
|
||||
rope = GlmImageRotaryPosEmbed(dim=40, patch_size=2, theta=10000.0)
|
||||
hidden_states = torch.randn(1, 16, 64, 64) # [B, C, H, W]
|
||||
|
||||
cos, sin = rope(hidden_states)
|
||||
|
||||
# After patchify: height=32, width=32
|
||||
# Output: [32*32, dim] = [1024, 40]
|
||||
assert cos.shape[0] == 1024
|
||||
assert cos.shape[1] == 40
|
||||
assert sin.shape == cos.shape
|
||||
|
||||
def test_rope_value_range(self):
|
||||
"""Verify RoPE produces values in valid range."""
|
||||
rope = GlmImageRotaryPosEmbed(dim=40, patch_size=2, theta=10000.0)
|
||||
hidden_states = torch.randn(2, 16, 32, 32)
|
||||
|
||||
cos, sin = rope(hidden_states)
|
||||
|
||||
# cos and sin should be in [-1, 1]
|
||||
assert cos.min() >= -1.0 and cos.max() <= 1.0
|
||||
assert sin.min() >= -1.0 and sin.max() <= 1.0
|
||||
|
||||
def test_rope_consistency(self):
|
||||
"""Verify RoPE is consistent for same input."""
|
||||
rope = GlmImageRotaryPosEmbed(dim=40, patch_size=2, theta=10000.0)
|
||||
hidden_states = torch.randn(1, 16, 32, 32)
|
||||
|
||||
cos1, sin1 = rope(hidden_states)
|
||||
cos2, sin2 = rope(hidden_states)
|
||||
|
||||
# Same input should produce same output
|
||||
assert torch.allclose(cos1, cos2)
|
||||
assert torch.allclose(sin1, sin2)
|
||||
|
||||
|
||||
class TestGlmImageTransformer2DModelQuantization:
|
||||
"""Test GlmImageTransformer2DModel with quantization config."""
|
||||
|
||||
def test_accepts_quant_config_parameter(self, mocker: MockerFixture):
|
||||
"""Verify the model accepts quant_config parameter."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
# Create a minimal mock od_config
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2 # Small number for testing
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=mock_quant_config,
|
||||
)
|
||||
|
||||
# Check that quantization config was passed to transformer blocks
|
||||
for block in model.transformer_blocks:
|
||||
assert block.norm1.linear.quant_config is mock_quant_config
|
||||
assert block.attn1.to_qkv.quant_config is mock_quant_config
|
||||
assert block.ff.net[0].proj.quant_config is mock_quant_config
|
||||
|
||||
def test_accepts_none_quant_config(self, mocker: MockerFixture):
|
||||
"""Verify quant_config=None is accepted."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
# Check that quantization config is None
|
||||
for block in model.transformer_blocks:
|
||||
assert block.norm1.linear.quant_config is None
|
||||
assert block.attn1.to_qkv.quant_config is None
|
||||
|
||||
def test_norm_out_has_no_quantization(self, mocker: MockerFixture):
|
||||
"""Verify norm_out (output layer) does NOT use quantization to preserve precision."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
mock_quant_config = mocker.MagicMock()
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=mock_quant_config,
|
||||
)
|
||||
|
||||
# norm_out.linear should NOT have quant_config to preserve output precision
|
||||
assert model.norm_out.linear.quant_config is None
|
||||
|
||||
def test_model_has_sp_plan(self, mocker: MockerFixture):
|
||||
"""Verify model has _sp_plan defined for sequence parallelism."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
# Verify _sp_plan exists
|
||||
assert hasattr(model, "_sp_plan")
|
||||
assert "prepare" in model._sp_plan
|
||||
assert "proj_out" in model._sp_plan
|
||||
|
||||
def test_model_has_hsdp_shard_conditions(self, mocker: MockerFixture):
|
||||
"""Verify model has _hsdp_shard_conditions for HSDP parallelism."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
assert hasattr(model, "_hsdp_shard_conditions")
|
||||
assert len(model._hsdp_shard_conditions) > 0
|
||||
|
||||
def test_model_creates_kv_cache(self, mocker: MockerFixture):
|
||||
"""Verify model can create KV cache for image editing."""
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
parallel_config = DiffusionParallelConfig(
|
||||
tensor_parallel_size=1,
|
||||
sequence_parallel_size=1,
|
||||
)
|
||||
|
||||
mock_tf_config = mocker.MagicMock()
|
||||
mock_tf_config.patch_size = 2
|
||||
mock_tf_config.in_channels = 16
|
||||
mock_tf_config.out_channels = 16
|
||||
mock_tf_config.num_attention_heads = 64
|
||||
mock_tf_config.attention_head_dim = 40
|
||||
mock_tf_config.time_embed_dim = 512
|
||||
mock_tf_config.condition_dim = 256
|
||||
mock_tf_config.prior_vq_quantizer_codebook_size = 16384
|
||||
mock_tf_config.text_embed_dim = 1024
|
||||
mock_tf_config.num_layers = 2
|
||||
|
||||
mock_od_config = mocker.MagicMock(spec=OmniDiffusionConfig)
|
||||
mock_od_config.tf_model_config = mock_tf_config
|
||||
mock_od_config.parallel_config = parallel_config
|
||||
|
||||
model = GlmImageTransformer2DModel(
|
||||
od_config=mock_od_config,
|
||||
quant_config=None,
|
||||
)
|
||||
|
||||
kv_cache = model.create_kv_cache()
|
||||
assert kv_cache is not None
|
||||
assert len(kv_cache) == 2 # num_layers
|
||||
@@ -339,6 +339,7 @@ class TestCFGParallelForwardPath:
|
||||
audio_latents=audio_latents,
|
||||
output_type="latent",
|
||||
),
|
||||
request_id="ltx23-cfg-parallel-forward-test",
|
||||
)
|
||||
|
||||
output = pipe.forward(req)
|
||||
|
||||
@@ -167,6 +167,7 @@ def test_basic_generation(ovis_pipeline):
|
||||
# Setup request
|
||||
req = OmniDiffusionRequest(
|
||||
prompts=["A photo of a cat"],
|
||||
request_id="ovis-basic",
|
||||
sampling_params=OmniDiffusionSamplingParams(
|
||||
height=256,
|
||||
width=256,
|
||||
@@ -206,6 +207,7 @@ def test_guidance_scale(ovis_pipeline, monkeypatch: pytest.MonkeyPatch):
|
||||
"negative_prompt": "bad quality",
|
||||
}
|
||||
],
|
||||
request_id="ovis-guidance",
|
||||
sampling_params=OmniDiffusionSamplingParams(
|
||||
height=256,
|
||||
width=256,
|
||||
@@ -225,6 +227,7 @@ def test_resolution_check(ovis_pipeline):
|
||||
# Pass odd resolution
|
||||
req = OmniDiffusionRequest(
|
||||
prompts=["test"],
|
||||
request_id="ovis-resolution",
|
||||
sampling_params=OmniDiffusionSamplingParams(
|
||||
height=250, # Not divisible by 16 (8*2)
|
||||
width=250,
|
||||
|
||||
@@ -22,15 +22,15 @@ class DiffusionSchedulerOutput:
|
||||
num_waiting_reqs: int = 0
|
||||
|
||||
@property
|
||||
def scheduled_req_ids(self):
|
||||
def scheduled_request_ids(self):
|
||||
ids = [req.request_id for req in self.scheduled_new_reqs]
|
||||
if self.scheduled_cached_reqs:
|
||||
ids.extend(self.scheduled_cached_reqs.sched_req_ids)
|
||||
ids.extend(self.scheduled_cached_reqs.request_ids)
|
||||
return ids
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
return len(self.scheduled_req_ids) == 0
|
||||
return len(self.scheduled_request_ids) == 0
|
||||
|
||||
|
||||
class MockScheduler:
|
||||
@@ -71,31 +71,36 @@ async def test_async_add_req_and_wait_for_response():
|
||||
engine.scheduler = MockScheduler()
|
||||
engine._out_queue = {}
|
||||
engine.abort_queue: queue.Queue[str] = queue.Queue()
|
||||
engine._rpc_lock = threading.Lock()
|
||||
engine._rpc_queue = queue.Queue()
|
||||
engine._rpc_lock = threading.RLock()
|
||||
engine._cv = threading.Condition(engine._rpc_lock)
|
||||
engine._init_lock = asyncio.Lock()
|
||||
engine._closed = False
|
||||
engine._loop_started = False
|
||||
engine.main_loop = None
|
||||
|
||||
engine._finalize_finished_request = lambda rid, out, err: out.result
|
||||
|
||||
def mock_execute_batch(sched_output):
|
||||
req_ids = sched_output.scheduled_req_ids
|
||||
request_ids = sched_output.scheduled_request_ids
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
class MockRunnerOutput:
|
||||
def __init__(self, ids):
|
||||
self.req_id = ids
|
||||
self.request_id = ids
|
||||
self.step_index = 0
|
||||
self.finished = True
|
||||
self._results = {rid: SimpleNamespace(result_data=f"data_{rid}") for rid in ids}
|
||||
|
||||
def get_req_output(self, rid):
|
||||
def get_request_output(self, rid):
|
||||
return SimpleNamespace(result=self._results[rid], step_index=0, finished=True)
|
||||
|
||||
return MockRunnerOutput(req_ids)
|
||||
return MockRunnerOutput(request_ids)
|
||||
|
||||
engine.execute_fn = mock_execute_batch
|
||||
|
||||
engine.stop_event = threading.Event()
|
||||
engine.start_background_loop()
|
||||
await engine._check_and_start_background_loop()
|
||||
|
||||
async def run_task(rid):
|
||||
req = SimpleNamespace(request_id=rid)
|
||||
@@ -105,10 +110,13 @@ async def test_async_add_req_and_wait_for_response():
|
||||
|
||||
task_ids = [f"req_{i}" for i in range(5)]
|
||||
tasks = [run_task(rid) for rid in task_ids]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
engine.stop_event.set()
|
||||
engine.worker_thread.join(timeout=5)
|
||||
try:
|
||||
results = await asyncio.gather(*tasks)
|
||||
finally:
|
||||
with engine._cv:
|
||||
engine.stop_event.set()
|
||||
engine._cv.notify_all()
|
||||
engine.worker_thread.join(timeout=5)
|
||||
assert len(results) == 5
|
||||
for rid, res, elapsed in results:
|
||||
assert rid in res.result_data
|
||||
|
||||
@@ -17,15 +17,10 @@ from vllm_omni.inputs.data import OmniDiffusionSamplingParams
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu, pytest.mark.diffusion]
|
||||
|
||||
|
||||
def _make_request(
|
||||
request_id: str,
|
||||
*,
|
||||
request_ids: list[str] | None = None,
|
||||
) -> OmniDiffusionRequest:
|
||||
def _make_request(request_id: str) -> OmniDiffusionRequest:
|
||||
return OmniDiffusionRequest(
|
||||
prompts=[f"prompt_{request_id}"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=request_ids or [request_id],
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
@@ -79,15 +74,15 @@ def test_handle_finished_requests_ignores_already_drained_waiter() -> None:
|
||||
engine._finalize_finished_request.assert_not_called()
|
||||
|
||||
|
||||
def test_abort_parent_request_id_aborts_batched_scheduler_request() -> None:
|
||||
def test_abort_request_id_aborts_scheduler_request() -> None:
|
||||
engine = _make_engine()
|
||||
request = _make_request("batch-parent", request_ids=["batch-parent-0", "batch-parent-1"])
|
||||
sched_req_id = engine.scheduler.add_request(request)
|
||||
request = _make_request("batch-parent")
|
||||
request_id = engine.scheduler.add_request(request)
|
||||
|
||||
engine.abort("batch-parent")
|
||||
engine._process_aborts_queue()
|
||||
|
||||
state = engine.scheduler.get_request_state(sched_req_id)
|
||||
state = engine.scheduler.get_request_state(request_id)
|
||||
assert state is not None
|
||||
assert state.status == DiffusionRequestStatus.FINISHED_ABORTED
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class _ConcurrencyTrackingExecutor:
|
||||
if method in {"execute_model", "execute_stepwise", "generate"}:
|
||||
# args[0] is the request-like object for execute_model.
|
||||
req = args[0] if args else None
|
||||
tag = req.request_ids[0] if req is not None and hasattr(req, "request_ids") else "unknown"
|
||||
tag = req.request_id if req is not None and hasattr(req, "request_id") else "unknown"
|
||||
return DiffusionOutput(error=f"result_for_{tag}")
|
||||
tag = args[0] if args else method
|
||||
return DiffusionOutput(error=f"rpc_result_for_{tag}")
|
||||
@@ -108,7 +108,7 @@ class _ConcurrencyTrackingExecutor:
|
||||
exec_all_ranks=True,
|
||||
)
|
||||
return RunnerOutput(
|
||||
req_id=new_req.sched_req_id,
|
||||
request_id=new_req.request_id,
|
||||
step_index=None,
|
||||
finished=True,
|
||||
result=result,
|
||||
@@ -120,7 +120,7 @@ class _ConcurrencyTrackingExecutor:
|
||||
|
||||
def _make_request(tag: str):
|
||||
return SimpleNamespace(
|
||||
request_ids=[tag],
|
||||
request_id=tag,
|
||||
prompts=[f"prompt_{tag}"],
|
||||
sampling_params=SimpleNamespace(num_inference_steps=1, **_SAMPLING_KEY_DEFAULTS),
|
||||
)
|
||||
@@ -148,6 +148,7 @@ def _make_engine_with_loop(
|
||||
engine._rpc_lock = threading.RLock()
|
||||
engine._cv = threading.Condition(engine._rpc_lock)
|
||||
engine._out_queue = {}
|
||||
engine._closed = False
|
||||
engine.abort_queue = queue.Queue()
|
||||
engine._rpc_queue = queue.Queue()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import random
|
||||
|
||||
import pytest
|
||||
|
||||
from vllm_omni.diffusion.request import OmniDiffusionRequest
|
||||
from vllm_omni.diffusion.request import DUMMY_DIFFUSION_REQUEST_ID, OmniDiffusionRequest
|
||||
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu]
|
||||
@@ -15,9 +15,53 @@ def _make_request() -> OmniDiffusionRequest:
|
||||
return OmniDiffusionRequest(
|
||||
prompts=[{"prompt": "a cup of coffee on a table"}],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_id="request-test",
|
||||
)
|
||||
|
||||
|
||||
def test_request_id_is_required():
|
||||
with pytest.raises(TypeError, match="request_id"):
|
||||
OmniDiffusionRequest(
|
||||
prompts=[{"prompt": "a cup of coffee on a table"}],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
)
|
||||
|
||||
|
||||
def test_request_ids_identity_list_is_removed():
|
||||
req = _make_request()
|
||||
|
||||
assert req.request_id == "request-test"
|
||||
assert not hasattr(req, "request_ids")
|
||||
|
||||
|
||||
def test_request_id_must_be_non_empty():
|
||||
with pytest.raises(ValueError, match="request_id must be a non-empty string"):
|
||||
OmniDiffusionRequest(
|
||||
prompts=[{"prompt": "a cup of coffee on a table"}],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_id="",
|
||||
)
|
||||
|
||||
|
||||
def test_dummy_run_request_is_identified_by_reserved_request_id():
|
||||
req = OmniDiffusionRequest(
|
||||
prompts=[{"prompt": "dummy run"}],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_id=DUMMY_DIFFUSION_REQUEST_ID,
|
||||
)
|
||||
|
||||
assert req.is_dummy_run()
|
||||
assert OmniDiffusionRequest.is_dummy_run_request_id(DUMMY_DIFFUSION_REQUEST_ID)
|
||||
|
||||
|
||||
def test_non_dummy_request_is_not_identified_as_dummy_run():
|
||||
req = _make_request()
|
||||
|
||||
assert not req.is_dummy_run()
|
||||
assert not OmniDiffusionRequest.is_dummy_run_request_id(req.request_id)
|
||||
assert not OmniDiffusionRequest.is_dummy_run_request_id(None)
|
||||
|
||||
|
||||
def test_tp_seed_same_across_ranks_and_varies_across_requests():
|
||||
random.seed(0)
|
||||
n_requests = 5
|
||||
|
||||
@@ -31,13 +31,13 @@ def _make_request(req_id: str) -> OmniDiffusionRequest:
|
||||
return OmniDiffusionRequest(
|
||||
prompts=[f"prompt_{req_id}"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=[req_id],
|
||||
request_id=req_id,
|
||||
)
|
||||
|
||||
|
||||
def _make_request_output(req_id: str, *, error: str | None = None, finished: bool = True):
|
||||
return RunnerOutput(
|
||||
req_id=req_id,
|
||||
request_id=req_id,
|
||||
step_index=None,
|
||||
finished=finished,
|
||||
result=DiffusionOutput(output=None, error=error),
|
||||
@@ -52,7 +52,7 @@ def _make_step_output(
|
||||
error: str | None = None,
|
||||
):
|
||||
return RunnerOutput(
|
||||
req_id=req_id,
|
||||
request_id=req_id,
|
||||
step_index=step_index,
|
||||
finished=finished,
|
||||
result=DiffusionOutput(output=None, error=error) if error is not None else None,
|
||||
@@ -73,16 +73,16 @@ def _make_step_request(
|
||||
num_inference_steps=num_inference_steps,
|
||||
step_index=step_index,
|
||||
),
|
||||
request_ids=[req_id],
|
||||
request_id=req_id,
|
||||
)
|
||||
|
||||
|
||||
def _new_ids(sched_output) -> list[str]:
|
||||
return [req.sched_req_id for req in sched_output.scheduled_new_reqs]
|
||||
return [req.request_id for req in sched_output.scheduled_new_reqs]
|
||||
|
||||
|
||||
def _cached_ids(sched_output) -> list[str]:
|
||||
return list(sched_output.scheduled_cached_reqs.sched_req_ids)
|
||||
return list(sched_output.scheduled_cached_reqs.request_ids)
|
||||
|
||||
|
||||
class _StubScheduler(SchedulerInterface):
|
||||
@@ -90,7 +90,7 @@ class _StubScheduler(SchedulerInterface):
|
||||
self._request = request
|
||||
self._output = output
|
||||
self.initialized_with = None
|
||||
self._sched_req_id = request.request_ids[0]
|
||||
self._request_id = request.request_id
|
||||
self._state = None
|
||||
self._scheduled = False
|
||||
self.max_num_running_reqs = 1
|
||||
@@ -100,22 +100,22 @@ class _StubScheduler(SchedulerInterface):
|
||||
|
||||
def add_request(self, request: OmniDiffusionRequest) -> str:
|
||||
assert request is self._request
|
||||
self._state = SimpleNamespace(sched_req_id=self._sched_req_id, req=request)
|
||||
return self._sched_req_id
|
||||
self._state = SimpleNamespace(request_id=self._request_id, req=request)
|
||||
return self._request_id
|
||||
|
||||
def schedule(self):
|
||||
if self._scheduled or self._state is None:
|
||||
return SimpleNamespace(
|
||||
scheduled_new_reqs=[],
|
||||
scheduled_cached_reqs=CachedRequestData.make_empty(),
|
||||
scheduled_req_ids=[],
|
||||
scheduled_request_ids=[],
|
||||
is_empty=True,
|
||||
)
|
||||
self._scheduled = True
|
||||
return SimpleNamespace(
|
||||
scheduled_new_reqs=[NewRequestData.from_state(self._state)],
|
||||
scheduled_cached_reqs=CachedRequestData.make_empty(),
|
||||
scheduled_req_ids=[self._state.sched_req_id],
|
||||
scheduled_request_ids=[self._state.request_id],
|
||||
is_empty=False,
|
||||
)
|
||||
|
||||
@@ -123,30 +123,25 @@ class _StubScheduler(SchedulerInterface):
|
||||
del sched_output
|
||||
assert output is self._output
|
||||
self._state.status = DiffusionRequestStatus.FINISHED_COMPLETED
|
||||
return {self._sched_req_id}
|
||||
return {self._request_id}
|
||||
|
||||
def has_requests(self) -> bool:
|
||||
return not self._scheduled
|
||||
|
||||
def get_request_state(self, sched_req_id: str):
|
||||
del sched_req_id
|
||||
def get_request_state(self, request_id: str):
|
||||
del request_id
|
||||
return self._state
|
||||
|
||||
def get_sched_req_id(self, request_id: str) -> str | None:
|
||||
if request_id in self._request.request_ids:
|
||||
return self._sched_req_id
|
||||
return None
|
||||
|
||||
def pop_request_state(self, sched_req_id: str):
|
||||
del sched_req_id
|
||||
def pop_request_state(self, request_id: str):
|
||||
del request_id
|
||||
return self._state
|
||||
|
||||
def preempt_request(self, sched_req_id: str) -> bool:
|
||||
del sched_req_id
|
||||
def preempt_request(self, request_id: str) -> bool:
|
||||
del request_id
|
||||
return False
|
||||
|
||||
def finish_requests(self, sched_req_ids, status) -> None:
|
||||
del sched_req_ids, status
|
||||
def finish_requests(self, request_ids, status) -> None:
|
||||
del request_ids, status
|
||||
return None
|
||||
|
||||
def close(self) -> None:
|
||||
@@ -171,7 +166,7 @@ class TestGetSamplingParamsKey:
|
||||
return OmniDiffusionRequest(
|
||||
prompts=["prompt"],
|
||||
sampling_params=sp,
|
||||
request_ids=[f"req-{lora_int_id}-{lora_scale}"],
|
||||
request_id=f"req-{lora_int_id}-{lora_scale}",
|
||||
)
|
||||
|
||||
def test_distinguishes_lora_id(self) -> None:
|
||||
@@ -291,7 +286,7 @@ class TestRequestScheduler:
|
||||
OmniDiffusionRequest(
|
||||
prompts=["prompt_b"],
|
||||
sampling_params=OmniDiffusionSamplingParams(width=768),
|
||||
request_ids=["b"],
|
||||
request_id="b",
|
||||
)
|
||||
)
|
||||
scheduler.add_request(_make_request("c"))
|
||||
@@ -333,7 +328,7 @@ class TestRequestScheduler:
|
||||
assert state_a.status == DiffusionRequestStatus.FINISHED_ABORTED
|
||||
|
||||
assert self.scheduler.has_requests() is False
|
||||
assert self.scheduler.schedule().scheduled_req_ids == []
|
||||
assert self.scheduler.schedule().scheduled_request_ids == []
|
||||
|
||||
def test_has_requests_state_transition(self) -> None:
|
||||
assert self.scheduler.has_requests() is False
|
||||
@@ -348,60 +343,28 @@ class TestRequestScheduler:
|
||||
assert self.scheduler.get_request_state(req_id).status == DiffusionRequestStatus.FINISHED_COMPLETED
|
||||
assert self.scheduler.has_requests() is False
|
||||
|
||||
def test_request_id_mapping_lifecycle(self) -> None:
|
||||
def test_request_id_is_scheduler_key(self) -> None:
|
||||
request = OmniDiffusionRequest(
|
||||
prompts=["prompt_map_a", "prompt_map_b"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=["map-a", "map-b"],
|
||||
request_id="map-parent",
|
||||
)
|
||||
|
||||
sched_req_id = self.scheduler.add_request(request)
|
||||
request_id = self.scheduler.add_request(request)
|
||||
|
||||
assert self.scheduler.get_sched_req_id("map-a") == sched_req_id
|
||||
assert self.scheduler.get_sched_req_id("map-b") == sched_req_id
|
||||
assert self.scheduler.get_sched_req_id("map-parent") == sched_req_id
|
||||
assert request_id == "map-parent"
|
||||
state = self.scheduler.get_request_state("map-parent")
|
||||
assert state.request_id == "map-parent"
|
||||
|
||||
self.scheduler.pop_request_state(sched_req_id)
|
||||
self.scheduler.pop_request_state("map-parent")
|
||||
|
||||
assert self.scheduler.get_sched_req_id("map-a") is None
|
||||
assert self.scheduler.get_sched_req_id("map-b") is None
|
||||
assert self.scheduler.get_sched_req_id("map-parent") is None
|
||||
assert self.scheduler.get_request_state("map-parent") is None
|
||||
|
||||
def test_parent_request_id_registration_failure_rolls_back_child_ids(self) -> None:
|
||||
self.scheduler.add_request(
|
||||
OmniDiffusionRequest(
|
||||
prompts=["prompt_existing"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=["existing-child"],
|
||||
request_id="duplicate-parent",
|
||||
)
|
||||
)
|
||||
def test_duplicate_request_id_is_rejected(self) -> None:
|
||||
self.scheduler.add_request(_make_request("dup"))
|
||||
|
||||
colliding_request = OmniDiffusionRequest(
|
||||
prompts=["prompt_unique"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=["unique-child"],
|
||||
request_id="duplicate-parent",
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="duplicate-parent"):
|
||||
self.scheduler.add_request(colliding_request)
|
||||
|
||||
assert self.scheduler.get_request_state("unique-child") is None
|
||||
assert self.scheduler.get_sched_req_id("unique-child") is None
|
||||
|
||||
valid_request = OmniDiffusionRequest(
|
||||
prompts=["prompt_unique"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
request_ids=["unique-child"],
|
||||
request_id="unique-parent",
|
||||
)
|
||||
sched_req_id = self.scheduler.add_request(valid_request)
|
||||
|
||||
assert sched_req_id == "unique-child"
|
||||
assert self.scheduler.get_sched_req_id("unique-child") == sched_req_id
|
||||
assert self.scheduler.get_sched_req_id("unique-parent") == sched_req_id
|
||||
with pytest.raises(ValueError, match="request_id 'dup' is already active"):
|
||||
self.scheduler.add_request(_make_request("dup"))
|
||||
|
||||
|
||||
class TestDiffusionEngine:
|
||||
@@ -662,7 +625,7 @@ class TestStepScheduler:
|
||||
finished = self.scheduler.update_from_output(
|
||||
sched_output,
|
||||
RunnerOutput(
|
||||
req_id=req_id,
|
||||
request_id=req_id,
|
||||
step_index=None,
|
||||
finished=True,
|
||||
result=None,
|
||||
@@ -934,7 +897,7 @@ class TestStepScheduler:
|
||||
|
||||
for _ in range(expected_steps - 1):
|
||||
sched_output = self.scheduler.schedule()
|
||||
assert sched_output.scheduled_req_ids == [req_id]
|
||||
assert sched_output.scheduled_request_ids == [req_id]
|
||||
next_step = request.sampling_params.step_index + 1
|
||||
assert (
|
||||
self.scheduler.update_from_output(
|
||||
@@ -945,7 +908,7 @@ class TestStepScheduler:
|
||||
)
|
||||
|
||||
final_output = self.scheduler.schedule()
|
||||
assert final_output.scheduled_req_ids == [req_id]
|
||||
assert final_output.scheduled_request_ids == [req_id]
|
||||
assert self.scheduler.update_from_output(
|
||||
final_output,
|
||||
_make_step_output(req_id, step_index=expected_steps, finished=True),
|
||||
|
||||
@@ -206,7 +206,7 @@ class _DistributedStepPipeline(CFGParallelMixin):
|
||||
def _make_step_request(num_inference_steps: int = 2):
|
||||
return SimpleNamespace(
|
||||
prompts=["a prompt"],
|
||||
request_ids=["req-1"],
|
||||
request_id="req-1",
|
||||
sampling_params=SimpleNamespace(
|
||||
generator=None,
|
||||
seed=None,
|
||||
@@ -227,7 +227,7 @@ def _make_engine_request(req_id: str = "req-1", num_inference_steps: int = 2) ->
|
||||
return OmniDiffusionRequest(
|
||||
prompts=[f"prompt-{req_id}"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=num_inference_steps),
|
||||
request_ids=[req_id],
|
||||
request_id=req_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -274,10 +274,10 @@ def _make_distributed_runner(mode: str, device: torch.device):
|
||||
return runner
|
||||
|
||||
|
||||
def _make_scheduler_output(req, sched_req_id="req-1", step_id=0, finished_req_ids=None):
|
||||
def _make_scheduler_output(req, request_id="req-1", step_id=0, finished_req_ids=None):
|
||||
return DiffusionSchedulerOutput(
|
||||
step_id=step_id,
|
||||
scheduled_new_reqs=[NewRequestData(sched_req_id=sched_req_id, req=req)],
|
||||
scheduled_new_reqs=[NewRequestData(request_id=request_id, req=req)],
|
||||
scheduled_cached_reqs=CachedRequestData.make_empty(),
|
||||
finished_req_ids=set() if finished_req_ids is None else set(finished_req_ids),
|
||||
num_running_reqs=1,
|
||||
@@ -287,7 +287,7 @@ def _make_scheduler_output(req, sched_req_id="req-1", step_id=0, finished_req_id
|
||||
|
||||
def _make_batch_scheduler_output(reqs, *, step_id=0, finished_req_ids=None):
|
||||
"""Scheduler output for a homogeneous batch (one NewRequestData per req)."""
|
||||
new_reqs = [NewRequestData(sched_req_id=r.request_ids[0], req=r) for r in reqs]
|
||||
new_reqs = [NewRequestData(request_id=r.request_id, req=r) for r in reqs]
|
||||
return DiffusionSchedulerOutput(
|
||||
step_id=step_id,
|
||||
scheduled_new_reqs=new_reqs,
|
||||
@@ -298,11 +298,11 @@ def _make_batch_scheduler_output(reqs, *, step_id=0, finished_req_ids=None):
|
||||
)
|
||||
|
||||
|
||||
def _make_cached_scheduler_output(sched_req_id="req-1", step_id=1, finished_req_ids=None):
|
||||
def _make_cached_scheduler_output(request_id="req-1", step_id=1, finished_req_ids=None):
|
||||
return DiffusionSchedulerOutput(
|
||||
step_id=step_id,
|
||||
scheduled_new_reqs=[],
|
||||
scheduled_cached_reqs=CachedRequestData(sched_req_ids=[sched_req_id]),
|
||||
scheduled_cached_reqs=CachedRequestData(request_ids=[request_id]),
|
||||
finished_req_ids=set() if finished_req_ids is None else set(finished_req_ids),
|
||||
num_running_reqs=1,
|
||||
num_waiting_reqs=0,
|
||||
@@ -359,7 +359,7 @@ def _distributed_step_worker(local_rank: int, world_size: int, mode: str, master
|
||||
runner,
|
||||
_make_scheduler_output(_make_step_request(num_inference_steps=1), step_id=0),
|
||||
)
|
||||
output = result.get_req_output("req-1")
|
||||
output = result.get_request_output("req-1")
|
||||
|
||||
assert output.finished is True
|
||||
assert output.result is not None
|
||||
@@ -384,16 +384,16 @@ class TestRunner:
|
||||
monkeypatch.setattr(model_runner_module, "set_forward_context", _noop_forward_context)
|
||||
|
||||
result = DiffusionModelRunner.execute_stepwise(runner, _make_scheduler_output(req, step_id=0))
|
||||
first = result.get_req_output("req-1")
|
||||
assert first.req_id == "req-1"
|
||||
first = result.get_request_output("req-1")
|
||||
assert first.request_id == "req-1"
|
||||
assert first.step_index == 1
|
||||
assert first.finished is False
|
||||
assert first.result is None
|
||||
assert "req-1" in runner.state_cache
|
||||
|
||||
result = DiffusionModelRunner.execute_stepwise(runner, _make_cached_scheduler_output(step_id=1))
|
||||
second = result.get_req_output("req-1")
|
||||
assert second.req_id == "req-1"
|
||||
second = result.get_request_output("req-1")
|
||||
assert second.request_id == "req-1"
|
||||
assert second.step_index == 2
|
||||
assert second.finished is True
|
||||
assert second.result is not None
|
||||
@@ -410,13 +410,13 @@ class TestRunner:
|
||||
runner = _make_runner()
|
||||
req_1 = _make_step_request()
|
||||
req_2 = _make_step_request()
|
||||
req_2.request_ids = ["req-2"]
|
||||
req_2.request_id = "req-2"
|
||||
|
||||
scheduler_output = DiffusionSchedulerOutput(
|
||||
step_id=0,
|
||||
scheduled_new_reqs=[
|
||||
NewRequestData(sched_req_id="req-1", req=req_1),
|
||||
NewRequestData(sched_req_id="req-2", req=req_2),
|
||||
NewRequestData(request_id="req-1", req=req_1),
|
||||
NewRequestData(request_id="req-2", req=req_2),
|
||||
],
|
||||
scheduled_cached_reqs=CachedRequestData.make_empty(),
|
||||
finished_req_ids=set(),
|
||||
@@ -431,7 +431,7 @@ class TestRunner:
|
||||
runner = _make_runner()
|
||||
|
||||
with pytest.raises(ValueError, match="Missing cached state"):
|
||||
DiffusionModelRunner.execute_stepwise(runner, _make_cached_scheduler_output(sched_req_id="req-missing"))
|
||||
DiffusionModelRunner.execute_stepwise(runner, _make_cached_scheduler_output(request_id="req-missing"))
|
||||
|
||||
def test_interrupt_marks_request_finished_and_clears_state(self, monkeypatch):
|
||||
runner = _make_runner()
|
||||
@@ -440,8 +440,8 @@ class TestRunner:
|
||||
monkeypatch.setattr(model_runner_module, "set_forward_context", _noop_forward_context)
|
||||
|
||||
result = DiffusionModelRunner.execute_stepwise(runner, _make_scheduler_output(req, step_id=0))
|
||||
output = result.get_req_output("req-1")
|
||||
assert output.req_id == "req-1"
|
||||
output = result.get_request_output("req-1")
|
||||
assert output.request_id == "req-1"
|
||||
assert output.step_index == 0
|
||||
assert output.finished is True
|
||||
assert output.result is not None
|
||||
@@ -515,7 +515,7 @@ def _make_step_worker(lora_manager=None, *, expected_output=None):
|
||||
worker = object.__new__(DiffusionWorker)
|
||||
worker.lora_manager = lora_manager
|
||||
worker._step_lora_state = {}
|
||||
output = expected_output if expected_output is not None else RunnerOutput(req_id="req-1")
|
||||
output = expected_output if expected_output is not None else RunnerOutput(request_id="req-1")
|
||||
worker.model_runner = SimpleNamespace(execute_stepwise=lambda arg: output)
|
||||
return worker
|
||||
|
||||
@@ -525,9 +525,9 @@ class TestWorker:
|
||||
"""DiffusionWorker.execute_stepwise"""
|
||||
|
||||
def test_delegates_to_model_runner(self):
|
||||
expected = RunnerOutput(req_id="req-1", step_index=1, finished=False, result=None)
|
||||
expected = RunnerOutput(request_id="req-1", step_index=1, finished=False, result=None)
|
||||
worker = _make_step_worker(expected_output=expected)
|
||||
scheduler_output = _make_scheduler_output(_make_engine_request("req-1"), sched_req_id="req-1")
|
||||
scheduler_output = _make_scheduler_output(_make_engine_request("req-1"), request_id="req-1")
|
||||
|
||||
output = DiffusionWorker.execute_stepwise(worker, scheduler_output)
|
||||
|
||||
@@ -536,7 +536,7 @@ class TestWorker:
|
||||
def test_deactivates_lora_when_request_has_no_adapter(self):
|
||||
manager = _RecordingLoRAManager()
|
||||
worker = _make_step_worker(lora_manager=manager)
|
||||
scheduler_output = _make_scheduler_output(_make_engine_request("req-1"), sched_req_id="req-1")
|
||||
scheduler_output = _make_scheduler_output(_make_engine_request("req-1"), request_id="req-1")
|
||||
|
||||
DiffusionWorker.execute_stepwise(worker, scheduler_output)
|
||||
|
||||
@@ -552,7 +552,7 @@ class TestWorker:
|
||||
|
||||
manager = _RecordingLoRAManager()
|
||||
worker = _make_step_worker(lora_manager=manager)
|
||||
scheduler_output = _make_scheduler_output(request, sched_req_id="req-1")
|
||||
scheduler_output = _make_scheduler_output(request, request_id="req-1")
|
||||
|
||||
DiffusionWorker.execute_stepwise(worker, scheduler_output)
|
||||
|
||||
@@ -568,8 +568,8 @@ class TestWorker:
|
||||
|
||||
manager = _RecordingLoRAManager()
|
||||
worker = _make_step_worker(lora_manager=manager)
|
||||
first = _make_scheduler_output(request, sched_req_id="req-1")
|
||||
second = _make_cached_scheduler_output(sched_req_id="req-1", step_id=1)
|
||||
first = _make_scheduler_output(request, request_id="req-1")
|
||||
second = _make_cached_scheduler_output(request_id="req-1", step_id=1)
|
||||
|
||||
DiffusionWorker.execute_stepwise(worker, first)
|
||||
DiffusionWorker.execute_stepwise(worker, second)
|
||||
@@ -610,10 +610,10 @@ class TestWorker:
|
||||
next_request.sampling_params.lora_request = lora_request
|
||||
|
||||
worker = _make_step_worker(lora_manager=_RecordingLoRAManager())
|
||||
first = _make_scheduler_output(finishing, sched_req_id="req-1")
|
||||
first = _make_scheduler_output(finishing, request_id="req-1")
|
||||
next_batch = _make_scheduler_output(
|
||||
next_request,
|
||||
sched_req_id="req-2",
|
||||
request_id="req-2",
|
||||
step_id=1,
|
||||
finished_req_ids={"req-1"},
|
||||
)
|
||||
@@ -633,11 +633,11 @@ class TestExecutor:
|
||||
def test_execute_step_passes_through_runner_output(self, mocker: MockerFixture):
|
||||
executor = object.__new__(MultiprocDiffusionExecutor)
|
||||
executor._ensure_open = lambda: None
|
||||
expected = RunnerOutput(req_id="req-step", step_index=1, finished=False, result=None)
|
||||
expected = RunnerOutput(request_id="req-step", step_index=1, finished=False, result=None)
|
||||
executor.collective_rpc = mocker.Mock(return_value=expected)
|
||||
|
||||
request = _make_engine_request("req-step", num_inference_steps=2)
|
||||
scheduler_output = _make_scheduler_output(request, sched_req_id="req-step")
|
||||
scheduler_output = _make_scheduler_output(request, request_id="req-step")
|
||||
|
||||
output = MultiprocDiffusionExecutor.execute_step(executor, scheduler_output)
|
||||
|
||||
@@ -653,7 +653,7 @@ class TestEngine:
|
||||
[
|
||||
(
|
||||
lambda _: RunnerOutput(
|
||||
req_id="req-error",
|
||||
request_id="req-error",
|
||||
step_index=1,
|
||||
finished=True,
|
||||
result=DiffusionOutput(error="boom"),
|
||||
@@ -688,7 +688,7 @@ class TestEngine:
|
||||
call_count["n"] += 1
|
||||
finished = call_count["n"] == 2
|
||||
return RunnerOutput(
|
||||
req_id="req-step",
|
||||
request_id="req-step",
|
||||
step_index=call_count["n"],
|
||||
finished=finished,
|
||||
result=(DiffusionOutput(output=torch.tensor([2.0])) if finished else None),
|
||||
@@ -714,7 +714,7 @@ class TestEngine:
|
||||
step["n"] += 1
|
||||
engine.abort("req-stop")
|
||||
return RunnerOutput(
|
||||
req_id="req-stop",
|
||||
request_id="req-stop",
|
||||
step_index=1,
|
||||
finished=False,
|
||||
result=None,
|
||||
@@ -741,7 +741,7 @@ class TestEngine:
|
||||
assert sched_output == _make_cached_scheduler_output("req-mid", step_id=1)
|
||||
engine.abort("req-mid")
|
||||
return RunnerOutput(
|
||||
req_id="req-mid",
|
||||
request_id="req-mid",
|
||||
step_index=step["n"],
|
||||
finished=False,
|
||||
result=None,
|
||||
@@ -760,7 +760,7 @@ class TestEngine:
|
||||
engine = _make_engine(
|
||||
scheduler,
|
||||
execute_fn=lambda _: RunnerOutput(
|
||||
req_id="req-missing",
|
||||
request_id="req-missing",
|
||||
step_index=1,
|
||||
finished=True,
|
||||
result=None,
|
||||
@@ -777,7 +777,7 @@ class TestEngine:
|
||||
class TestIPC:
|
||||
def test_pack_unpack_runner_output_shm(self):
|
||||
tensor = torch.zeros(300_000, dtype=torch.float32)
|
||||
output = RunnerOutput(req_id="req-1", finished=True, result=DiffusionOutput(output=tensor))
|
||||
output = RunnerOutput(request_id="req-1", finished=True, result=DiffusionOutput(output=tensor))
|
||||
|
||||
packed = pack_diffusion_output_shm(output)
|
||||
assert isinstance(packed.result.output, dict)
|
||||
|
||||
@@ -36,7 +36,7 @@ def _tagged_output(tag: str) -> DiffusionOutput:
|
||||
def _mock_request(tag: str):
|
||||
"""Return a lightweight request object identifiable by *tag*."""
|
||||
return SimpleNamespace(
|
||||
request_ids=[tag],
|
||||
request_id=tag,
|
||||
prompts=[f"prompt_{tag}"],
|
||||
sampling_params=OmniDiffusionSamplingParams(num_inference_steps=1),
|
||||
)
|
||||
@@ -97,8 +97,8 @@ def _start_worker(req_q, res_q, count=2):
|
||||
req = req_q.get(timeout=10)
|
||||
method = req.get("method", "")
|
||||
args = req.get("args", ())
|
||||
if method in {"generate", "execute_model"} and args and hasattr(args[0], "request_ids"):
|
||||
tag = f"result_for_{args[0].request_ids[0]}"
|
||||
if method in {"generate", "execute_model"} and args and hasattr(args[0], "request_id"):
|
||||
tag = f"result_for_{args[0].request_id}"
|
||||
elif args:
|
||||
tag = f"result_for_{args[0]}"
|
||||
else:
|
||||
|
||||
@@ -68,7 +68,7 @@ def test_process_batch_request_preserves_parent_request_id_and_kv_sender_info():
|
||||
|
||||
request = captured["request"]
|
||||
assert request.request_id == "req-parent"
|
||||
assert request.request_ids == ["req-parent-0", "req-parent-1"]
|
||||
assert not hasattr(request, "request_ids")
|
||||
assert request.kv_sender_info == {0: {"host": "10.0.0.2", "zmq_port": 50151}}
|
||||
assert result.request_id == "req-parent"
|
||||
assert result.images == ["img-1", "img-2"]
|
||||
|
||||
Reference in new issue
Block a user