import os import json import types import torch from mmgp import offload from shared.utils import files_locator as fl from shared.utils.utils import convert_image_to_tensor, convert_tensor_to_image from transformers import AutoTokenizer, PreTrainedTokenizerBase, Qwen2VLImageProcessorFast, Qwen2VLProcessor from transformers.processing_utils import ProcessorMixin from .pipeline import DEFAULT_TIMESTEPS, NOISE_SCALE, generate_image, resample_timesteps from .qwen3_vl_configuration import register_qwen3_vl_config from .qwen3_vl_transformers import Qwen3VLForConditionalGeneration HIDREAM_QUANTO_BF16_EXCLUDE = [ "model.language_model.layers.*.mlp.down_proj.weight", "model.language_model.layers.*.self_attn.o_proj.weight", ] class HiDreamQwen3VLProcessor(Qwen2VLProcessor): attributes = ["image_processor", "tokenizer"] def __init__(self, image_processor=None, tokenizer=None, chat_template=None, **kwargs): self.image_token = "<|image_pad|>" if not hasattr(tokenizer, "image_token") else tokenizer.image_token self.video_token = "<|video_pad|>" if not hasattr(tokenizer, "video_token") else tokenizer.video_token self.image_token_id = tokenizer.image_token_id if getattr(tokenizer, "image_token_id", None) else tokenizer.convert_tokens_to_ids(self.image_token) self.video_token_id = tokenizer.video_token_id if getattr(tokenizer, "video_token_id", None) else tokenizer.convert_tokens_to_ids(self.video_token) ProcessorMixin.__init__(self, image_processor, tokenizer, chat_template=chat_template) def add_special_tokens(tokenizer): tokenizer.boi_token = "<|boi_token|>" tokenizer.bor_token = "<|bor_token|>" tokenizer.eor_token = "<|eor_token|>" tokenizer.bot_token = "<|bot_token|>" tokenizer.tms_token = "<|tms_token|>" def get_tokenizer(processor): if isinstance(processor, PreTrainedTokenizerBase): return processor return processor.tokenizer def load_processor(processor_path): tokenizer = AutoTokenizer.from_pretrained(processor_path, trust_remote_code=True) image_processor = Qwen2VLImageProcessorFast.from_pretrained(processor_path) chat_template = getattr(tokenizer, "chat_template", None) chat_template_path = os.path.join(processor_path, "chat_template.json") if chat_template is None and os.path.isfile(chat_template_path): with open(chat_template_path, "r", encoding="utf-8") as reader: chat_template = json.load(reader).get("chat_template") return HiDreamQwen3VLProcessor(image_processor=image_processor, tokenizer=tokenizer, chat_template=chat_template) def _as_pil(image): return convert_tensor_to_image(image) if torch.is_tensor(image) else image def _quantized_transformer_filename(model_filename, dtype): model_filename = os.path.basename(model_filename) if dtype == torch.bfloat16: model_filename = model_filename.replace("fp16", "bf16").replace("FP16", "bf16") elif dtype == torch.float16: model_filename = model_filename.replace("bf16", "fp16").replace("BF16", "fp16") for rep in ["mfp16", "fp16", "mbf16", "bf16"]: if "_" + rep in model_filename: return model_filename.replace("_" + rep, "_quanto_" + rep + "_int8") pos = model_filename.rfind(".") return model_filename[:pos] + "_quanto_int8" + model_filename[pos:] if pos >= 0 else model_filename + "_quanto_int8" def save_quantized_transformer(model, model_filename, dtype, config_file): if "quanto" in model_filename: return None quantized_filename = _quantized_transformer_filename(model_filename, dtype) existing_path = fl.locate_file(quantized_filename, error_if_none=False) if existing_path is not None: print(f"There isn't any model to quantize as quantized model '{quantized_filename}' already exists") return existing_path quantized_path = fl.get_download_location(quantized_filename) os.makedirs(os.path.dirname(quantized_path), exist_ok=True) offload.save_model(model, quantized_path, do_quantize=True, config_file_path=config_file, quantize_exclude=HIDREAM_QUANTO_BF16_EXCLUDE) print(f"New quantized file '{quantized_filename}' had been created.") return quantized_path def _attach_lora_preprocessor(transformer): def preprocess_loras(self, model_type, sd): if not sd: return sd qwen3_model_prefixes = ( "visual.", "language_model.", "t_embedder1.", "t_embedder2.", "x_embedder.", "final_layer2.", ) wrapper_prefixes = ("diffusion_model.", "transformer.") new_sd = {} for key, value in sd.items(): for wrapper_prefix in wrapper_prefixes: if key.startswith(wrapper_prefix): inner_key = key[len(wrapper_prefix):] if inner_key.startswith(qwen3_model_prefixes): key = wrapper_prefix + "model." + inner_key break else: if key.startswith(qwen3_model_prefixes): key = "model." + key new_sd[key] = value return new_sd transformer.preprocess_loras = types.MethodType(preprocess_loras, transformer) class model_factory: def __init__( self, checkpoint_dir, model_filename=None, model_type=None, model_def=None, base_model_type=None, quantizeTransformer=False, dtype=torch.bfloat16, save_quantized=False, **kwargs, ): model_def = model_def or {} transformer_filename = model_filename[0] if isinstance(model_filename, (list, tuple)) else model_filename if transformer_filename is None: raise ValueError("No transformer filename provided for HiDream O1.") self.model_type = model_type self.base_model_type = base_model_type self.model_def = model_def self.dtype = dtype self._abort = False processor_folder = model_def.get("processor_folder", base_model_type) processor_path = os.path.dirname(fl.locate_file(os.path.join(processor_folder, "tokenizer_config.json"))) config_path = fl.locate_file(os.path.join(processor_folder, "config.json")) register_qwen3_vl_config() self.processor = load_processor(processor_path) self.tokenizer = get_tokenizer(self.processor) add_special_tokens(self.tokenizer) source = model_def.get("source", None) load_filename = fl.locate_file(source) if source is not None else transformer_filename self.transformer = offload.fast_load_transformers_model( load_filename, writable_tensors=False, modelClass=Qwen3VLForConditionalGeneration, defaultConfigPath=config_path, default_dtype=dtype, ignore_unused_weights=True, do_quantize=quantizeTransformer and not save_quantized, ) self.transformer.eval().requires_grad_(False) self.model = self.transformer _attach_lora_preprocessor(self.transformer) self._set_interrupt(False) if source is not None: from wgp import save_model save_model(self.transformer, model_type, dtype, config_path) if save_quantized: save_quantized_transformer(self.transformer, transformer_filename, dtype, config_path) def generate( self, input_prompt="", alt_prompt="", image_start=None, image_end=None, input_frames=None, input_frames2=None, input_ref_images=None, input_ref_masks=None, input_masks=None, input_masks2=None, input_video=None, input_faces=None, input_custom=None, denoising_strength=1.0, masking_strength=1.0, prefix_frames_count=0, frame_num=1, batch_size=1, height=1024, width=1024, fit_into_canvas=None, shift=None, sample_solver="default", sampling_steps=50, guide_scale=5.0, guide2_scale=5.0, guide3_scale=5.0, switch_threshold=0, switch2_threshold=0, guide_phases=1, model_switch_phase=1, embedded_guidance_scale=0.0, n_prompt=None, seed=None, callback=None, enable_RIFLEx=False, VAE_tile_size=None, joint_pass=True, perturbation_switch=0, perturbation_layers=None, perturbation_start=0.0, perturbation_end=1.0, apg_switch=0, cfg_star_switch=0, cfg_zero_step=-1, alt_guide_scale=1.0, audio_cfg_scale=4.0, input_waveform=None, input_waveform_sample_rate=0, audio_guide=None, audio_guide2=None, audio_prompt_type="", audio_proj=None, audio_scale=None, audio_context_lens=None, context_scale=None, control_scale_alt=1.0, alt_scale=0.0, motion_amplitude=1.0, model_mode=0, causal_block_size=5, causal_attention=True, fps=1, overlapped_latents=None, return_latent_slice=False, overlap_noise=0, overlap_size=0, color_correction_strength=0, conditioning_latents_size=0, input_video_is_hdr=False, lora_dir=None, keep_frames_parsed=None, model_filename=None, model_type=None, loras_slists=None, NAG_scale=1.0, NAG_tau=3.5, NAG_alpha=0.5, speakers_bboxes=None, image_mode=1, video_prompt_type="", window_no=1, offloadobj=None, set_header_text=None, pre_video_frame=None, prefix_video=None, original_input_ref_images=None, image_refs_relative_size=50, outpainting_dims=None, face_arc_embeds=None, custom_settings=None, temperature=0.8, window_start_frame_no=0, input_video_strength=1.0, self_refiner_setting=0, self_refiner_plan="", self_refiner_f_uncertainty=0.0, self_refiner_certain_percentage=0.999, duration_seconds=0, pause_seconds=0, top_p=0.9, top_k=50, set_progress_status=None, loras_selected=None, frames_relative_positions_list=None, frames_to_inject=None, **kwargs ): self._set_interrupt(False) is_dev = self.base_model_type == "hidream_o1_dev" custom_settings = custom_settings or {} sampling_steps = int(sampling_steps) if seed is None or int(seed) < 0: seed = int(torch.seed() % (2**31 - 1)) else: seed = int(seed) if is_dev: scheduler_name = "flash" timesteps_list = resample_timesteps(DEFAULT_TIMESTEPS, sampling_steps) guide_scale = 0.0 shift = 1.0 if shift is None else shift noise_scale_start = float(custom_settings.get("noise_scale_start", 7.5)) noise_scale_end = float(custom_settings.get("noise_scale_end", 7.5)) noise_clip_std = float(custom_settings.get("noise_clip_std", 2.5)) else: scheduler_name = "default" timesteps_list = None shift = 3.0 if shift is None else shift noise_scale_start = float(custom_settings.get("noise_scale_start", NOISE_SCALE)) noise_scale_end = float(custom_settings.get("noise_scale_end", NOISE_SCALE)) noise_clip_std = float(custom_settings.get("noise_clip_std", 0.0)) ref_images = [] if image_start is not None: ref_images.append(_as_pil(image_start)) if input_frames is not None: ref_images.append(_as_pil(input_frames)) image_ref_source = original_input_ref_images if original_input_ref_images else input_ref_images if image_ref_source is not None: ref_images.extend(_as_pil(img) for img in image_ref_source) batch_size = max(1, int(batch_size)) with torch.inference_mode(): try: images = generate_image( model=self.transformer, processor=self.processor, prompt=input_prompt, ref_images=ref_images, height=height, width=width, num_inference_steps=sampling_steps, guidance_scale=guide_scale, shift=shift, timesteps_list=timesteps_list, scheduler_name=scheduler_name, seed=seed, noise_scale_start=noise_scale_start, noise_scale_end=noise_scale_end, noise_clip_std=noise_clip_std, keep_original_aspect=False, batch_size=batch_size, joint_pass=joint_pass, callback=callback, abort_callback=lambda: self._interrupt, ) finally: if hasattr(self.transformer, "clear_runtime_caches"): self.transformer.clear_runtime_caches() if images is None: return None if not isinstance(images, list): images = [images] images = [convert_image_to_tensor(image) for image in images] return torch.stack(images, dim=1) def get_loras_transformer(self, *args, **kwargs): return [], [] def _set_interrupt(self, value): self._abort = bool(value) for module in ( getattr(self, "transformer", None), getattr(getattr(self, "transformer", None), "model", None), getattr(getattr(self, "transformer", None), "visual", None), getattr(getattr(self, "transformer", None), "language_model", None), ): if module is not None: setattr(module, "_interrupt", self._abort) @property def _interrupt(self): return self._abort @_interrupt.setter def _interrupt(self, value): self._set_interrupt(value)