| from __future__ import annotations |
|
|
| import json |
| from pathlib import Path |
| from typing import Callable |
|
|
| import torch |
| from huggingface_hub import hf_hub_download |
| from transformers import ( |
| AutoModelForImageTextToText, |
| AutoProcessor, |
| BitsAndBytesConfig as TransformersBitsAndBytesConfig, |
| ) |
|
|
| from diffusers import ( |
| BitsAndBytesConfig as DiffusersBitsAndBytesConfig, |
| GGUFQuantizationConfig, |
| LTX2Pipeline, |
| LTX2VideoTransformer3DModel, |
| ) |
| from diffusers.quantizers import PipelineQuantizationConfig |
|
|
|
|
| LogFn = Callable[[str], None] |
|
|
|
|
| def maybe_enable_attention_backend(pipe, attention_backend: str) -> dict: |
| state = {"requested": attention_backend, "active": "sdpa", "status": "default"} |
| if attention_backend in {"", "sdpa", "default"}: |
| return state |
| if attention_backend not in {"flash3", "flash3_hub", "_flash_3_hub"}: |
| raise RuntimeError(f"Unsupported LTX25_ATTENTION_BACKEND={attention_backend!r}") |
|
|
| from kernels import get_kernel |
|
|
| get_kernel("kernels-community/flash-attn3", version=1) |
| pipe.transformer.set_attention_backend("_flash_3_hub") |
| state.update(status="enabled", active="_flash_3_hub") |
| return state |
|
|
|
|
| def component_config_path(model_dir: str | Path, subfolder: str) -> Path: |
| return Path(model_dir) / subfolder / "config.json" |
|
|
|
|
| def read_quantization_config(config_path: str | Path) -> dict | None: |
| path = Path(config_path) |
| if not path.exists(): |
| raise FileNotFoundError(f"Component config not found: {path}") |
| data = json.loads(path.read_text(encoding="utf-8")) |
| value = data.get("quantization_config") |
| return value if isinstance(value, dict) else None |
|
|
|
|
| def quantization_kind(config: dict | None) -> str: |
| if not config: |
| return "unquantized" |
| quant_method = str(config.get("quant_method") or "").strip().lower() |
| load_in_4bit = bool(config.get("load_in_4bit") or config.get("_load_in_4bit")) |
| quant_type = str(config.get("bnb_4bit_quant_type") or "").strip().lower() |
| if (load_in_4bit or quant_method in {"bitsandbytes_4bit", "bitsandbytes"}) and quant_type == "nf4": |
| return "bitsandbytes_nf4" |
| if load_in_4bit or quant_method in {"bitsandbytes_4bit", "bitsandbytes"}: |
| return f"bitsandbytes_4bit:{quant_type or 'unknown'}" |
| return quant_method or "unknown_prequantized" |
|
|
|
|
| def nf4_config(component: str): |
| if component == "transformer": |
| return DiffusersBitsAndBytesConfig( |
| load_in_4bit=True, |
| bnb_4bit_quant_type="nf4", |
| bnb_4bit_compute_dtype=torch.bfloat16, |
| bnb_4bit_use_double_quant=False, |
| ) |
| if component in {"text_encoder", "prompt_enhancer"}: |
| return TransformersBitsAndBytesConfig( |
| load_in_4bit=True, |
| bnb_4bit_quant_type="nf4", |
| bnb_4bit_compute_dtype=torch.bfloat16, |
| bnb_4bit_use_double_quant=False, |
| ) |
| raise ValueError(f"Unknown NF4 component: {component}") |
|
|
|
|
| def quantization_decision(config_path: str | Path, component: str, policy: str): |
| qconfig = read_quantization_config(config_path) |
| kind = quantization_kind(qconfig) |
| if policy == "repo_native": |
| return None, f"repo-native ({kind})" |
| if kind == "unquantized": |
| return nf4_config(component), "on-load BitsAndBytes NF4 / BF16 compute" |
| if kind == "bitsandbytes_nf4": |
| return None, "repo-prequantized BitsAndBytes NF4" |
| raise RuntimeError( |
| f"{component} uses unsupported prequantized format {kind!r} under quantization policy 'nf4_auto'. " |
| "Use a BitsAndBytes NF4 checkpoint, an unquantized checkpoint, or explicitly opt into repo_native." |
| ) |
|
|
|
|
| def remote_component_config( |
| repo_id: str, |
| subfolder: str | None, |
| revision: str | None, |
| *, |
| token: str | None, |
| ) -> Path: |
| filename = f"{subfolder.strip('/')}/config.json" if subfolder else "config.json" |
| return Path( |
| hf_hub_download( |
| repo_id=repo_id, |
| filename=filename, |
| revision=revision or None, |
| token=token, |
| ) |
| ) |
|
|
|
|
| def load_prompt_enhancer_cpu( |
| record: dict, |
| *, |
| repo_id: str, |
| revision: str | None, |
| enabled: bool, |
| policy: str, |
| token: str | None, |
| log_fn: LogFn, |
| ): |
| repo_id = str(repo_id or "").strip() |
| revision = str(revision or "").strip() or None |
| if not enabled: |
| record["effective"] = {"kind": "disabled", "reason": "disabled by space_config.py"} |
| return None, None |
| if not repo_id: |
| raise RuntimeError("PROMPT_ENHANCER_REPO_ID must not be empty when prompt enhancement is enabled.") |
|
|
| log_fn( |
| f"[D1R8P3] prompt enhancer CPU/NF4 prepare repo_id={repo_id} revision={revision!r}" |
| ) |
|
|
| try: |
| import torchvision |
| from transformers import Gemma4Processor |
| except Exception as exc: |
| raise RuntimeError( |
| "Prompt enhancer Gemma4Processor vision preflight failed. " |
| "This Space requires torch==2.11.0 with torchvision==0.26.0. " |
| f"Underlying error: {type(exc).__name__}: {exc}" |
| ) from exc |
|
|
| processor = AutoProcessor.from_pretrained(repo_id, revision=revision, token=token) |
| config_path = remote_component_config(repo_id, None, revision, token=token) |
| quant_config, quant_desc = quantization_decision(config_path, "prompt_enhancer", policy) |
| kwargs = { |
| "revision": revision, |
| "token": token, |
| "dtype": torch.bfloat16, |
| "device_map": {"": "cpu"}, |
| "low_cpu_mem_usage": True, |
| } |
| if quant_config is not None: |
| kwargs["quantization_config"] = quant_config |
| model = AutoModelForImageTextToText.from_pretrained(repo_id, **kwargs) |
| model.eval() |
| record["effective"] = { |
| "kind": "dedicated_gemma4", |
| "repo_id": repo_id, |
| "revision": revision, |
| "quantization": quant_desc, |
| "dtype": "bfloat16 compute", |
| "residency": "startup RAM-ready / separate callback GPU-lazy", |
| "runtime_dependency": "Transformers only; no Unsloth package/runtime", |
| } |
| log_fn(f"[D1R8P3] prompt enhancer startup RAM-ready quantization={quant_desc}") |
| return model, processor |
|
|
|
|
| def load_transformer_override( |
| model_dir: str, |
| record: dict, |
| policy: str, |
| *, |
| repo_id: str, |
| path: str | None, |
| revision: str | None, |
| token: str | None, |
| log_fn: LogFn, |
| mark_fallback: Callable[[dict, str], None], |
| ): |
| repo_id = str(repo_id or "").strip() |
| path = str(path or "").strip() or None |
| revision = str(revision or "").strip() or None |
| if not repo_id: |
| return None |
|
|
| log_fn( |
| f"[MODEL_OVERRIDE] transformer requested repo_id={repo_id} path={path!r} revision={revision!r}" |
| ) |
| try: |
| if path and path.lower().endswith(".gguf"): |
| local = hf_hub_download( |
| repo_id=repo_id, |
| filename=path, |
| revision=revision, |
| token=token, |
| ) |
| model = LTX2VideoTransformer3DModel.from_single_file( |
| local, |
| config=str(model_dir), |
| subfolder="transformer", |
| quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16), |
| dtype=torch.bfloat16, |
| ) |
| record["effective"] = { |
| "kind": "override_gguf", |
| "repo_id": repo_id, |
| "path": path, |
| "revision": revision, |
| "quantization": "GGUF / BF16 compute", |
| } |
| log_fn("[MODEL_OVERRIDE] transformer override loaded as GGUF") |
| return model |
|
|
| config_path = remote_component_config(repo_id, path, revision, token=token) |
| quant_config, quant_desc = quantization_decision(config_path, "transformer", policy) |
| kwargs = { |
| "revision": revision, |
| "token": token, |
| "dtype": torch.bfloat16, |
| } |
| if path: |
| kwargs["subfolder"] = path |
| if quant_config is not None: |
| kwargs["quantization_config"] = quant_config |
| model = LTX2VideoTransformer3DModel.from_pretrained(repo_id, **kwargs) |
| record["effective"] = { |
| "kind": "override_pretrained", |
| "repo_id": repo_id, |
| "path": path, |
| "revision": revision, |
| "quantization": quant_desc, |
| } |
| log_fn(f"[MODEL_OVERRIDE] transformer override loaded quantization={quant_desc}") |
| return model |
| except Exception as exc: |
| reason = f"{type(exc).__name__}: {exc}" |
| log_fn(f"[MODEL_OVERRIDE] transformer override FAILED: {reason}") |
| mark_fallback(record, reason) |
| return None |
|
|
|
|
| def load_text_encoder_override( |
| record: dict, |
| policy: str, |
| *, |
| repo_id: str, |
| path: str | None, |
| revision: str | None, |
| token: str | None, |
| log_fn: LogFn, |
| mark_fallback: Callable[[dict, str], None], |
| ): |
| repo_id = str(repo_id or "").strip() |
| path = str(path or "").strip() or None |
| revision = str(revision or "").strip() or None |
| if not repo_id: |
| return None |
|
|
| log_fn( |
| f"[MODEL_OVERRIDE] text_encoder requested repo_id={repo_id} path={path!r} revision={revision!r}" |
| ) |
| try: |
| config_path = remote_component_config(repo_id, path, revision, token=token) |
| quant_config, quant_desc = quantization_decision(config_path, "text_encoder", policy) |
| kwargs = { |
| "revision": revision, |
| "token": token, |
| "dtype": torch.bfloat16, |
| } |
| if path: |
| kwargs["subfolder"] = path |
| if quant_config is not None: |
| kwargs["quantization_config"] = quant_config |
| model = AutoModelForImageTextToText.from_pretrained(repo_id, **kwargs) |
| record["effective"] = { |
| "kind": "override_pretrained_experimental", |
| "repo_id": repo_id, |
| "path": path, |
| "revision": revision, |
| "quantization": quant_desc, |
| } |
| log_fn(f"[MODEL_OVERRIDE] text_encoder override loaded quantization={quant_desc}") |
| return model |
| except Exception as exc: |
| reason = f"{type(exc).__name__}: {exc}" |
| log_fn(f"[MODEL_OVERRIDE] text_encoder override FAILED: {reason}") |
| mark_fallback(record, reason) |
| return None |
|
|
|
|
| def base_component_quantization(model_dir: str, component: str, policy: str): |
| config_path = component_config_path(model_dir, component) |
| return quantization_decision(config_path, component, policy) |
|
|
|
|
| def load_full_sft_transformer( |
| model_dir: str, |
| record: dict, |
| policy: str, |
| base_repo_id: str, |
| base_revision: str | None, |
| *, |
| path: str, |
| source_repo: str, |
| source_revision: str | None, |
| token: str | None, |
| runtime_profile: str, |
| ): |
| path = str(path or "transformer_full").strip().strip("/") |
| use_base_snapshot = ( |
| source_repo == str(base_repo_id).strip() |
| and source_revision == (str(base_revision or "").strip() or None) |
| ) |
| if use_base_snapshot: |
| source_root = model_dir |
| config_path = component_config_path(model_dir, path) |
| kwargs = {"subfolder": path, "dtype": torch.bfloat16} |
| else: |
| source_root = source_repo |
| config_path = remote_component_config(source_repo, path, source_revision, token=token) |
| kwargs = { |
| "subfolder": path, |
| "revision": source_revision, |
| "token": token, |
| "dtype": torch.bfloat16, |
| } |
| quant_config, quant_desc = quantization_decision(config_path, "transformer", policy) |
| if quant_config is not None: |
| kwargs["quantization_config"] = quant_config |
| model = LTX2VideoTransformer3DModel.from_pretrained(source_root, **kwargs) |
| record["requested"] = { |
| "repo_id": source_repo, |
| "path": path, |
| "revision": source_revision, |
| } |
| record["effective"] = { |
| "kind": "full_sft_base_component", |
| "repo_id": source_repo, |
| "path": path, |
| "revision": source_revision, |
| "quantization": quant_desc, |
| "profile": runtime_profile, |
| "source_transport": "base_snapshot_component" if use_base_snapshot else "component_native_from_pretrained", |
| } |
| return model |
|
|
|
|
| def prepare_full_sft_stage2_lora( |
| model_dir: str, |
| base_repo_id: str, |
| base_revision: str | None, |
| *, |
| repo_id: str, |
| revision: str | None, |
| weight_name: str, |
| token: str | None, |
| ) -> tuple[Path, str | None]: |
| weight_name = str(weight_name or "ltx-2.5-22b-distilled-lora-450-bf16.safetensors").strip() |
| if not weight_name: |
| raise RuntimeError("FULL_SFT_STAGE2_LORA_WEIGHT_NAME must not be empty in full_sft_nf4.") |
|
|
| same_base = ( |
| repo_id == str(base_repo_id).strip() |
| and revision == (str(base_revision or "").strip() or None) |
| ) |
| local = Path(model_dir) / weight_name if same_base else Path() |
| if not (same_base and local.is_file()): |
| local = Path( |
| hf_hub_download( |
| repo_id=repo_id, |
| filename=weight_name, |
| revision=revision, |
| token=token, |
| ) |
| ) |
| resolved_revision = None |
| parts = list(local.parts) |
| if "snapshots" in parts: |
| idx = parts.index("snapshots") |
| if idx + 1 < len(parts): |
| resolved_revision = parts[idx + 1] |
| return local, resolved_revision |
|
|
|
|
| def build_base_pipeline( |
| model_dir: str, |
| *, |
| transformer_override=None, |
| text_encoder_override=None, |
| policy: str = "nf4_auto", |
| auto_duration_enabled: bool, |
| ): |
| quant_mapping = {} |
| component_quantization = {} |
|
|
| kwargs = { |
| "processor": None, |
| "prompt_enhancer": None, |
| "dtype": torch.bfloat16, |
| } |
| if not auto_duration_enabled: |
| kwargs["duration_head"] = None |
| if transformer_override is not None: |
| kwargs["transformer"] = transformer_override |
| else: |
| transformer_quant, desc = base_component_quantization(model_dir, "transformer", policy) |
| component_quantization["transformer"] = desc |
| if transformer_quant is not None: |
| quant_mapping["transformer"] = transformer_quant |
|
|
| if text_encoder_override is not None: |
| kwargs["text_encoder"] = text_encoder_override |
| else: |
| text_quant, desc = base_component_quantization(model_dir, "text_encoder", policy) |
| component_quantization["text_encoder"] = desc |
| if text_quant is not None: |
| quant_mapping["text_encoder"] = text_quant |
|
|
| if quant_mapping: |
| kwargs["quantization_config"] = PipelineQuantizationConfig(quant_mapping=quant_mapping) |
|
|
| pipe = LTX2Pipeline.from_pretrained(model_dir, **kwargs) |
| return pipe, component_quantization |
|
|
|
|
| def try_build_base_pipeline( |
| model_dir: str, |
| *, |
| transformer_override=None, |
| text_encoder_override=None, |
| policy: str = "nf4_auto", |
| auto_duration_enabled: bool, |
| ): |
| try: |
| return ( |
| build_base_pipeline( |
| model_dir, |
| transformer_override=transformer_override, |
| text_encoder_override=text_encoder_override, |
| policy=policy, |
| auto_duration_enabled=auto_duration_enabled, |
| ), |
| None, |
| ) |
| except Exception as exc: |
| return None, f"{type(exc).__name__}: {exc}" |
|
|