| """Shared IC-LoRA runtime primitives. |
| |
| Keep task semantics (prompting, preprocessing, task evidence and UI policy) in the |
| individual task module. This module only owns reusable adapter/reference mechanics |
| that are expected to be shared by future IC-LoRA tabs. |
| """ |
| from __future__ import annotations |
|
|
| import gc |
| import time |
| from dataclasses import dataclass |
| from pathlib import Path |
|
|
| import numpy as np |
| import PIL.Image |
| import torch |
| from diffusers.pipelines.ltx2.pipeline_ltx2_ic_lora import LTX2ReferenceCondition |
|
|
|
|
| @dataclass(frozen=True) |
| class PreparedRequestFiles: |
| """Validated request-local files needed by an IC task GPU callback.""" |
|
|
| reference_cache: Path |
| local_lora: Path |
| video_path: Path |
| diagnostics_path: Path |
|
|
|
|
| def adapter_state(pipe) -> dict: |
| """Return the public adapter inventory without assuming one Diffusers version.""" |
| state = {} |
| for name in ("get_active_adapters", "get_list_adapters"): |
| fn = getattr(pipe, name, None) |
| if callable(fn): |
| try: |
| state[name] = fn() |
| except Exception as exc: |
| state[name] = {"error": f"{type(exc).__name__}: {exc}"} |
| return state |
|
|
|
|
| def adapter_names(state: dict) -> set[str]: |
| """Return adapter names from Diffusers public adapter-state snapshots.""" |
| names: set[str] = set() |
|
|
| def _collect(value) -> None: |
| if isinstance(value, dict): |
| for item in value.values(): |
| _collect(item) |
| elif isinstance(value, (list, tuple, set)): |
| for item in value: |
| _collect(item) |
| elif isinstance(value, str) and value.strip() and not value.strip().lower().startswith("error"): |
| names.add(value.strip()) |
|
|
| _collect(state.get("get_active_adapters")) |
| _collect(state.get("get_list_adapters")) |
| return names |
|
|
|
|
| def adapter_state_has_any(state: dict) -> bool: |
| """True when a Diffusers adapter-state snapshot contains a real adapter.""" |
| def _nonempty(value) -> bool: |
| if isinstance(value, dict): |
| return any(_nonempty(item) for item in value.values()) |
| if isinstance(value, (list, tuple, set)): |
| return len(value) > 0 |
| if isinstance(value, str): |
| return bool(value.strip()) and not value.strip().lower().startswith("error") |
| return bool(value) |
|
|
| return _nonempty(state.get("get_active_adapters")) or _nonempty(state.get("get_list_adapters")) |
|
|
|
|
| def load_adapter_weights(*, pipe, local_lora: Path, adapter_name: str) -> None: |
| """Load one already-local IC adapter; never performs Hub I/O.""" |
| pipe.load_lora_weights( |
| str(local_lora.parent), |
| weight_name=local_lora.name, |
| adapter_name=adapter_name, |
| ) |
|
|
|
|
| def activate_adapter(*, pipe, adapter_name: str, strength: float) -> None: |
| """Select and enable one loaded request-scoped IC adapter.""" |
| pipe.set_adapters([adapter_name], adapter_weights=[float(strength)]) |
| pipe.enable_lora() |
|
|
| def release_adapter(*, pipe, adapter_name: str, state_pipe=None) -> tuple[float, dict]: |
| """Disable/delete one request-scoped IC adapter and release cached CUDA memory.""" |
| started = time.monotonic() |
| pipe.disable_lora() |
| pipe.delete_adapters([adapter_name]) |
| gc.collect() |
| if torch.cuda.is_available(): |
| torch.cuda.empty_cache() |
| return time.monotonic() - started, adapter_state(state_pipe if state_pipe is not None else pipe) |
|
|
|
|
| def force_release_adapter(*, pipe, adapter_name: str) -> None: |
| """Best-effort finalizer used only after an IC callback failed mid-lifecycle.""" |
| try: |
| pipe.disable_lora() |
| pipe.delete_adapters([adapter_name]) |
| finally: |
| gc.collect() |
| if torch.cuda.is_available(): |
| torch.cuda.empty_cache() |
|
|
|
|
| def reference_condition_from_cache( |
| cache_path: Path, |
| *, |
| num_frames: int, |
| height: int, |
| width: int, |
| strength: float, |
| ) -> LTX2ReferenceCondition: |
| """Restore an RGB frame cache and build the standard LTX2 reference condition.""" |
| with np.load(cache_path, allow_pickle=False) as cached: |
| reference_array = np.asarray(cached["frames"], dtype=np.uint8) |
| expected_shape = (int(num_frames), int(height), int(width), 3) |
| if tuple(reference_array.shape) != expected_shape: |
| raise RuntimeError(f"Prepared reference shape mismatch: {reference_array.shape} != {expected_shape}") |
| reference_frames = [PIL.Image.fromarray(frame).convert("RGB") for frame in reference_array] |
| return LTX2ReferenceCondition(frames=reference_frames, strength=float(strength)) |
|
|