diff --git a/vllm_omni/deploy/hunyuan_image_3_moe.yaml b/vllm_omni/deploy/hunyuan_image_3_moe.yaml index 4ae4520a7..657af0481 100644 --- a/vllm_omni/deploy/hunyuan_image_3_moe.yaml +++ b/vllm_omni/deploy/hunyuan_image_3_moe.yaml @@ -106,11 +106,14 @@ platforms: xpu: stages: + # Both stages stay resident, so they cannot share cards. - stage_id: 0 gpu_memory_utilization: 0.95 - devices: "0,1,2,3,4,5,6,7" - tensor_parallel_size: 8 - max_num_batched_tokens: 32784 + devices: "0,1,2,3" + tensor_parallel_size: 4 + # fp8 AR weights leave under 1 GiB for KV; 22800 tokens will not fit. + max_num_batched_tokens: 8192 + max_model_len: 8192 quantization: fp8 enable_expert_parallel: true worker_cls: vllm_omni.platforms.xpu.worker.xpu_ar_worker.XPUARWorker @@ -120,12 +123,12 @@ platforms: repetition_penalty: 1.1 - stage_id: 1 gpu_memory_utilization: 0.9 - devices: "0,1,2,3,4,5,6,7" + devices: "4,5,6,7" quantization: fp8 parallel_config: pipeline_parallel_size: 1 data_parallel_size: 1 - tensor_parallel_size: 8 + tensor_parallel_size: 4 enable_expert_parallel: true sequence_parallel_size: 1 ulysses_degree: 1 diff --git a/vllm_omni/diffusion/models/hunyuan_image3/hunyuan_image3_transformer.py b/vllm_omni/diffusion/models/hunyuan_image3/hunyuan_image3_transformer.py index d58a43498..1e91f87c1 100644 --- a/vllm_omni/diffusion/models/hunyuan_image3/hunyuan_image3_transformer.py +++ b/vllm_omni/diffusion/models/hunyuan_image3/hunyuan_image3_transformer.py @@ -3040,7 +3040,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): # 3. negative cfg prefill # For CFG distilled models, skip negative CFG prefill (cfg_factor=1, no negative prompt) - if self.do_classifier_free_guidance and not self.model.config.cfg_distilled: + if self.do_classifier_free_guidance and not getattr(self.model.config, "cfg_distilled", False): self._maybe_run_negative_cfg_prefill( input_ids=input_ids, model_kwargs=model_kwargs, @@ -3151,7 +3151,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): # For CFG distilled models, skip CFG parallel (cfg_factor=1, no negative branch) cfg_parallel_ready = ( self.do_classifier_free_guidance - and not self.model.config.cfg_distilled + and not getattr(self.model.config, "cfg_distilled", False) and get_classifier_free_guidance_world_size() == 2 ) @@ -3207,7 +3207,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): self._split_model_kwargs_for_cfg_parallel(model_kwargs, batch_size, cfg_rank) else: # For CFG distilled models, cfg_factor is always 1 (CFG embedded in model) - if self.model.config.cfg_distilled: + if getattr(self.model.config, "cfg_distilled", False): cfg_factor = 1 else: cfg_factor = 1 + self.do_classifier_free_guidance @@ -3252,7 +3252,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): # Sequential CFG: double the batch latent_model_input = torch.cat([latents] * cfg_factor) - if self.model.config.use_meanflow: + if getattr(self.model.config, "use_meanflow", False): r = self.scheduler.get_timestep_r(t) r_expand = r.repeat(latent_model_input.shape[0]) else: @@ -3281,7 +3281,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): if should_compute: # Handle guidance for CFG distilled models - if self.model.config.cfg_distilled: + if getattr(self.model.config, "cfg_distilled", False): model_kwargs["guidance"] = torch.tensor( [1000.0 * self._guidance_scale], device=self.device, @@ -3308,7 +3308,7 @@ class HunyuanImage3Text2ImagePipeline(DiffusionPipeline): # Perform guidance # For CFG distilled models, guidance is already embedded in the model, # so we skip the explicit CFG computation - if self.model.config.cfg_distilled: + if getattr(self.model.config, "cfg_distilled", False): # CFG distilled: guidance is handled internally via guidance_emb pass elif cfg_parallel_ready: diff --git a/vllm_omni/diffusion/models/hunyuan_image3/pipeline_hunyuan_image3.py b/vllm_omni/diffusion/models/hunyuan_image3/pipeline_hunyuan_image3.py index fcfd48b4f..441a9636e 100644 --- a/vllm_omni/diffusion/models/hunyuan_image3/pipeline_hunyuan_image3.py +++ b/vllm_omni/diffusion/models/hunyuan_image3/pipeline_hunyuan_image3.py @@ -338,6 +338,9 @@ class HunyuanImage3Pipeline( supports_step_execution: ClassVar[bool] = True supports_request_batch = False support_image_input = True + # The warmup request's blank image routes this AR+diffusion model down its + # image-edit path, where the token block cannot match the latent patch grid. + dummy_run_num_frames: ClassVar[int] = 0 _dit_modules: ClassVar[list[str]] = ["model"] _encoder_modules: ClassVar[list[str]] = ["vision_model"] _vae_modules: ClassVar[list[str]] = ["vae"] diff --git a/vllm_omni/model_executor/models/hunyuan_image3/hunyuan_image3.py b/vllm_omni/model_executor/models/hunyuan_image3/hunyuan_image3.py index b15a19982..31225d7ad 100755 --- a/vllm_omni/model_executor/models/hunyuan_image3/hunyuan_image3.py +++ b/vllm_omni/model_executor/models/hunyuan_image3/hunyuan_image3.py @@ -1557,7 +1557,11 @@ class HunyuanImage3ForConditionalGeneration(nn.Module, SupportsMultiModal, Suppo self._end_ratio_id = tokenizer.convert_tokens_to_ids("") ratio_33 = tokenizer.convert_tokens_to_ids("") ratio_36 = tokenizer.convert_tokens_to_ids("") - self._ratio_other_slices = [(ratio_33, ratio_36 + 1)] + # The base checkpoint stops at ; only Instruct has 33-36. + if ratio_33 is None or ratio_36 is None: + self._ratio_other_slices = [] + else: + self._ratio_other_slices = [(ratio_33, ratio_36 + 1)] # Build the full set of ratio token IDs for use as stop tokens. self._all_ratio_ids = set(range(self._start_ratio_id, self._end_ratio_id + 1)) for s, e in self._ratio_other_slices: