[diffusion] fix: plumb max_sequence_length via diffusers_kwargs (#20930)

Co-authored-by: jiangqc <jqc1569978990@gmail.com>
Co-authored-by: jiangqianchen <jiangqianchen@xiaohongshu.com>
This commit is contained in:
jiangqc
2026-05-13 16:32:47 +08:00
committed by GitHub
co-authored by jiangqc jiangqianchen
parent 7d515c6d1f
commit d8f7b78a29
8 changed files with 42 additions and 4 deletions
@@ -349,6 +349,13 @@ class PipelineConfig:
def tokenize_prompt(self, prompt: list[str], tokenizer, tok_kwargs) -> dict:
return tokenizer(prompt, **tok_kwargs)
def is_flux_v1(self) -> bool:
"""True if this pipeline is FLUX v1 (dual CLIP + T5 text encoders).
Used by text encoding (e.g. fixed CLIP context). Other pipelines return False.
"""
return False
def prepare_latent_shape(self, batch, batch_size, num_frames):
height = batch.height // self.vae_config.arch_config.spatial_compression_ratio
width = batch.width // self.vae_config.arch_config.spatial_compression_ratio
@@ -87,6 +87,9 @@ class FluxPipelineConfig(ImagePipelineConfig):
]
)
def is_flux_v1(self) -> bool:
return True
def get_text_encoder_attention_mask(self, text_inputs, encoder_index):
# Flux v1 does not use attention masks for text encoders.
return None
@@ -472,6 +475,9 @@ class Flux2PipelineConfig(FluxPipelineConfig):
]
)
def is_flux_v1(self) -> bool:
return False
def get_text_encoder_attention_mask(self, text_inputs, encoder_index):
# Flux2 uses standard attention masks (unlike Flux v1).
return text_inputs.get("attention_mask")
@@ -490,6 +496,7 @@ class Flux2PipelineConfig(FluxPipelineConfig):
def tokenize_prompt(self, prompts: list[str], tokenizer, tok_kwargs) -> dict:
messages = build_flux2_text_messages(prompts)
effective_max_length = tok_kwargs.pop("max_length", 512)
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=False,
@@ -499,7 +506,7 @@ class Flux2PipelineConfig(FluxPipelineConfig):
padding="max_length",
truncation=True,
# 2048 from official github repo, 512 from diffusers
max_length=512,
max_length=effective_max_length,
)
return inputs
@@ -178,6 +178,16 @@ class QwenImagePipelineConfig(QwenImageRolloutPipelineMixin, ImagePipelineConfig
]
)
def tokenize_prompt(self, prompts: list[str], tokenizer, tok_kwargs) -> dict:
tok_kwargs.setdefault("truncation", True)
if tok_kwargs.get("max_length") is not None:
tok_kwargs["padding"] = "max_length"
else:
tok_kwargs.setdefault("max_length", 1024)
tok_kwargs["padding"] = True
return tokenizer(prompts, **tok_kwargs)
def prepare_sigmas(self, sigmas, num_inference_steps):
return self._prepare_sigmas(sigmas, num_inference_steps)
@@ -90,10 +90,12 @@ class ZImagePipelineConfig(ZImageRolloutPipelineMixin, ImagePipelineConfig):
)
for prompt in prompts
]
effective_max_length = tok_kwargs.pop("max_length", 512)
return tokenizer(
rendered_prompts,
padding="max_length",
max_length=512, # TODO (yhyang201): set max length according to config
max_length=effective_max_length,
truncation=True,
return_tensors="pt",
)
@@ -218,6 +218,7 @@ class SamplingParams:
return_file_paths_only: bool = True
enable_sequence_shard: bool | None = None
diffusers_kwargs: dict | None = None
# Prompt enhancement (ErnieImage)
use_pe: bool | None = None
@@ -39,11 +39,9 @@ from sglang.multimodal_gen.runtime.entrypoints.utils import prepare_request
from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import OutputBatch
from sglang.multimodal_gen.runtime.scheduler_client import async_scheduler_client
from sglang.multimodal_gen.runtime.server_args import get_global_server_args
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
from sglang.srt.observability.trace import extract_trace_headers
router = APIRouter(prefix="/v1/images", tags=["images"])
logger = init_logger(__name__)
def _get_extra_field(request, field_name):
@@ -153,6 +151,7 @@ async def generations(
enable_teacache=request.enable_teacache,
output_compression=request.output_compression,
output_quality=request.output_quality,
diffusers_kwargs=request.diffusers_kwargs,
enable_upscaling=request.enable_upscaling,
upscaling_model_path=request.upscaling_model_path,
upscaling_scale=request.upscaling_scale,
@@ -418,6 +418,9 @@ def prepare_request(
VSA_sparsity=server_args.attention_backend_config.VSA_sparsity,
)
sampling_params.apply_request_extra(req)
diffusers_kwargs = getattr(sampling_params, "diffusers_kwargs", None)
if diffusers_kwargs and "max_sequence_length" in diffusers_kwargs:
req.max_sequence_length = diffusers_kwargs["max_sequence_length"]
req.adjust_size(server_args)
@@ -185,6 +185,9 @@ class TextEncodingStage(PipelineStage):
all_indices: list[int] = list(range(len(self.text_encoders)))
# Get max_sequence_length from batch if available
max_seq_length = getattr(batch, "max_sequence_length", None)
(
prompt_embeds_list,
prompt_masks_list,
@@ -196,6 +199,7 @@ class TextEncodingStage(PipelineStage):
server_args,
encoder_index=all_indices,
return_attention_mask=True,
max_length=max_seq_length,
)
for pe in prompt_embeds_list:
@@ -475,6 +479,11 @@ class TextEncodingStage(PipelineStage):
encoder_config.tokenizer_kwargs,
**text_encoder_extra_arg,
)
# Pass max_length to tokenizer if specified in the request. Flux v1 encoder 0
# is CLIP with a fixed 77-token context; overriding breaks tokenization.
is_flux_v1 = server_args.pipeline_config.is_flux_v1()
if max_length is not None and not (is_flux_v1 and i == 0):
tok_kwargs["max_length"] = max_length
text_inputs: dict = server_args.pipeline_config.tokenize_prompt(
processed_text_list, tokenizer, tok_kwargs