CoolFace
Apppublic

Ani14/Video-agent

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
model_handler.py248 linesDownload Raw Back to root
1"""2Model handler for WAN-VACE video generation3"""4import torch5 6# -----------------------------------------------------------------------------7# XPU shim for CPU‑only environments8#9# Newer versions of `diffusers` attempt to call `torch.xpu.empty_cache()` for10# Intel GPU support. If the installed PyTorch build does not include XPU11# support (as is the case on CPU‑only environments), accessing `torch.xpu`12# results in an AttributeError. To avoid this, we define a dummy `xpu`13# namespace on the `torch` module when it is missing. This namespace14# implements the minimal methods used by `diffusers` (`empty_cache`,15# `is_available`, and `device_count`).16#17# Intel’s `intel-extension-for-pytorch` provides XPU support, but even when18# installed, some CPU builds of PyTorch may not expose `torch.xpu`. This19# shim ensures that the application runs regardless of whether XPU support is20# present.21# -----------------------------------------------------------------------------22if not hasattr(torch, "xpu"):23    class _DummyXPU:24        @staticmethod25        def empty_cache():26            return None27        @staticmethod28        def manual_seed(_seed: int):29            return None30        @staticmethod31        def is_available():32            return False33        @staticmethod34        def device_count():35            return 036        @staticmethod37        def current_device():38            return 039        @staticmethod40        def set_device(_idx: int):41            return None42    torch.xpu = _DummyXPU()  # type: ignore43import time44from typing import Optional, Tuple, Any45from transformers import UMT5EncoderModel46from diffusers import AutoencoderKLWan, WanVACEPipeline, WanVACETransformer3DModel, GGUFQuantizationConfig47from diffusers.schedulers.scheduling_unipc_multistep import UniPCMultistepScheduler48from diffusers.utils import export_to_video49from huggingface_hub import login50import gradio as gr51 52from config import MODEL_CONFIG, DEFAULT_PARAMS, HF_TOKEN53import os54from utils import create_temp_video_path, validate_generation_params, validate_prompt, format_generation_info55 56class WanVACEModelHandler:57    """Handler for WAN-VACE model loading and video generation"""58    59    def __init__(self):60        self.pipe = None61        self.is_loaded = False62        self.loading_progress = 063        64    def login_hf(self) -> bool:65        """Login to Hugging Face"""66        try:67            login(token=HF_TOKEN)68            return True69        except Exception as e:70            print(f"Warning: Could not login to Hugging Face: {e}")71            return False72    73    def load_model(self, progress_callback=None) -> Tuple[bool, str]:74        """Load the WAN-VACE model components"""75        try:76            # Login to HF77            self.login_hf()78            79            if progress_callback:80                progress_callback(0.1, "Loading transformer model...")81            82            # Determine desired dtype for CPU/GPU execution.  83            # Hugging Face Spaces often run on CPU, where bfloat16 may not be supported.  84            # Allow the dtype to be configured via the WAN_DTYPE environment variable.  85            # Supported values: "bfloat16" (default) or "float32".  86            dtype_str = os.getenv("WAN_DTYPE", "bfloat16").lower()87            # Select compute dtype: use bfloat16 only if requested and available.  88            # Fall back to float32 otherwise.  89            compute_dtype = torch.bfloat16 if dtype_str == "bfloat16" else torch.float3290            # Likewise for the torch dtype used when loading weights.  91            torch_dtype = compute_dtype92 93            # Load transformer94            transformer = WanVACETransformer3DModel.from_single_file(95                MODEL_CONFIG["transformer_path"],96                quantization_config=GGUFQuantizationConfig(compute_dtype=compute_dtype),97                torch_dtype=torch_dtype,98            )99            100            if progress_callback:101                progress_callback(0.4, "Loading text encoder...")102            103            # Load text encoder104            text_encoder = UMT5EncoderModel.from_pretrained(105                MODEL_CONFIG["text_encoder_path"],106                gguf_file=MODEL_CONFIG["text_encoder_file"],107                torch_dtype=torch_dtype,108            )109            110            if progress_callback:111                progress_callback(0.7, "Loading VAE...")112            113            # Load VAE114            vae = AutoencoderKLWan.from_pretrained(115                MODEL_CONFIG["vae_path"],116                subfolder="vae",117                torch_dtype=torch.float32118            )119            120            if progress_callback:121                progress_callback(0.9, "Assembling pipeline...")122            123            # Create pipeline124            self.pipe = WanVACEPipeline.from_pretrained(125                MODEL_CONFIG["pipeline_path"],126                transformer=transformer,127                text_encoder=text_encoder,128                vae=vae,129                torch_dtype=torch_dtype130            )131            132            # Configure scheduler133            flow_shift = DEFAULT_PARAMS["flow_shift"]134            self.pipe.scheduler = UniPCMultistepScheduler.from_config(135                self.pipe.scheduler.config, 136                flow_shift=flow_shift137            )138            139            # Enable optimizations140            self.pipe.enable_model_cpu_offload()141            self.pipe.vae.enable_tiling()142            143            self.is_loaded = True144            145            if progress_callback:146                progress_callback(1.0, "Model loaded successfully!")147            148            return True, "Model loaded successfully!"149            150        except Exception as e:151            error_msg = f"Error loading model: {str(e)}"152            if progress_callback:153                progress_callback(0, error_msg)154            return False, error_msg155    156    def generate_video(157        self,158        prompt: str,159        negative_prompt: str = "",160        width: int = DEFAULT_PARAMS["width"],161        height: int = DEFAULT_PARAMS["height"],162        num_frames: int = DEFAULT_PARAMS["num_frames"],163        num_inference_steps: int = DEFAULT_PARAMS["num_inference_steps"],164        guidance_scale: float = DEFAULT_PARAMS["guidance_scale"],165        seed: Optional[int] = None,166        progress_callback=None167    ) -> Tuple[bool, str, str, str]:168        """169        Generate video from text prompt170        Returns: (success, video_path, error_message, generation_info)171        """172        173        if not self.is_loaded:174            return False, "", "Model not loaded. Please load the model first.", ""175        176        # Validate inputs177        prompt_valid, prompt_error = validate_prompt(prompt)178        if not prompt_valid:179            return False, "", prompt_error or "Invalid prompt", ""180        181        params_valid, params_error = validate_generation_params(182            width, height, num_frames, num_inference_steps, guidance_scale183        )184        if not params_valid:185            return False, "", params_error or "Invalid parameters", ""186        187        try:188            if progress_callback:189                progress_callback(0.1, "Preparing generation...")190            191            # Check if pipeline is loaded192            if self.pipe is None:193                return False, "", "Pipeline not initialized. Please load the model first.", ""194            195            # Set up generator with seed196            generator = torch.Generator()197            if seed is not None:198                generator.manual_seed(seed)199            else:200                generator.manual_seed(0)  # Default seed201            202            if progress_callback:203                progress_callback(0.2, "Starting video generation...")204            205            start_time = time.time()206            207            # Generate video208            output = self.pipe(209                prompt=prompt,210                negative_prompt=negative_prompt if negative_prompt else None,211                width=width,212                height=height,213                num_frames=num_frames,214                num_inference_steps=num_inference_steps,215                guidance_scale=guidance_scale,216                conditioning_scale=DEFAULT_PARAMS["conditioning_scale"],217                generator=generator,218            ).frames[0]219            220            if progress_callback:221                progress_callback(0.8, "Exporting video...")222            223            # Export to video file224            output_path = create_temp_video_path()225            export_to_video(output, output_path, fps=DEFAULT_PARAMS["fps"])226            227            generation_time = time.time() - start_time228            229            if progress_callback:230                progress_callback(1.0, "Video generation complete!")231            232            # Format generation info233            gen_info = format_generation_info(234                prompt, negative_prompt, width, height, num_frames,235                num_inference_steps, guidance_scale, generation_time236            )237            238            return True, output_path, "", gen_info239            240        except Exception as e:241            error_msg = f"Error during video generation: {str(e)}"242            if progress_callback:243                progress_callback(0, error_msg)244            return False, "", error_msg, ""245 246# Global model handler instance247model_handler = WanVACEModelHandler()248