Video-to-Video
Diffusers
English
Chinese
video-editing
streaming
quantization
int8
text-stream-stripping
Instructions to use kylinfires/Joy-face_change_optimized with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use kylinfires/Joy-face_change_optimized with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("kylinfires/Joy-face_change_optimized", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
Download deploy/xvideo/serving/graph_runner.py from kylinfires/Joy-face_change_optimized: direct link, hf CLI and curl.
- Browser
- Download file 13.8 kB
-
https://huggingface.co/kylinfires/Joy-face_change_optimized/resolve/main/deploy/xvideo/serving/graph_runner.py
- Command line
-
hf download hf://kylinfires/Joy-face_change_optimized/deploy/xvideo/serving/graph_runner.py
-
curl -L -o graph_runner.py https://huggingface.co/kylinfires/Joy-face_change_optimized/resolve/main/deploy/xvideo/serving/graph_runner.py
13.8 kB
| from __future__ import annotations | |
| import os | |
| import time | |
| from typing import Any, Callable, Optional | |
| import torch | |
| from xvideo.models.dit.rope import apply_rotary_emb | |
| from xvideo.lowvram import tight_vram_enabled | |
| from xvideo.models.dit import kv_entry_from_tensors, kv_entry_tensors, set_text_kv_active | |
| GRAPH_ENV = "JOYOMNI_CUDA_GRAPH" | |
| TEXT_KV_ENV = "JOYOMNI_TEXT_KV" | |
| # The captured graph's KV pool is laid out as exactly [sink, prev1(, ref)]: | |
| # an eager window [0, k-1, k] of this length is the only shape it can serve. | |
| GRAPH_WINDOW_CHUNKS = 3 | |
| def graph_env_enabled() -> bool: | |
| if tight_vram_enabled() or os.environ.get("JOYOMNI_STREAM_DIT", "").strip().lower() in { | |
| "1", "true", "yes", "on" | |
| }: | |
| # Graph static buffers need the weights resident; streaming keeps them on host. | |
| return False | |
| return os.environ.get(GRAPH_ENV, "1").lower() in {"1", "true", "yes", "on"} | |
| def text_kv_env_enabled() -> bool: | |
| return os.environ.get(TEXT_KV_ENV, "0").lower() in {"1", "true", "yes", "on"} | |
| class StreamingGraphRunner: | |
| def __init__( | |
| self, | |
| transformer, | |
| *, | |
| chunk_tokens: int, | |
| ref_tokens: int, | |
| latent_shape: tuple[int, ...], | |
| txt_len: int, | |
| text_kv_by_step: Optional[list[dict[int, tuple[torch.Tensor, torch.Tensor]]]] = None, | |
| max_temporal_ids: int, | |
| pos_freqs: tuple[torch.Tensor, torch.Tensor], | |
| device: torch.device, | |
| dtype: torch.dtype, | |
| autocast_ctx: Callable[[], Any], | |
| ): | |
| self.transformer = transformer | |
| self.device = device | |
| self.dtype = dtype | |
| self.autocast_ctx = autocast_ctx | |
| self.num_layers = len(transformer.double_blocks) | |
| heads = int(transformer.config.heads_num) | |
| head_dim = int(transformer.config.hidden_size) // heads | |
| self.chunk_tokens = chunk_tokens | |
| self.ref_tokens = int(ref_tokens) | |
| self.txt_len = int(txt_len) | |
| # Static per-step text K/V buffers: the concatenation shape is baked into | |
| # the graph, the values are refreshed by bind_session() per session. | |
| self.text_kv_by_step: list[dict[int, tuple[torch.Tensor, torch.Tensor]]] = [ | |
| { | |
| layer_idx: ( | |
| torch.zeros_like(key, device=device, dtype=dtype), | |
| torch.zeros_like(value, device=device, dtype=dtype), | |
| ) | |
| for layer_idx, (key, value) in cache.items() | |
| } | |
| for cache in (text_kv_by_step or ()) | |
| ] | |
| self.text_kv_enabled = bool(self.text_kv_by_step) | |
| self.max_temporal_ids = int(max_temporal_ids) | |
| self.sink_off = 0 | |
| self.prev1_off = chunk_tokens | |
| self.ref_off = 2 * chunk_tokens | |
| self.pool_len = 2 * chunk_tokens + self.ref_tokens | |
| L = self.num_layers | |
| self.pool_k = torch.zeros(L, 1, self.pool_len, heads, head_dim, device=device, dtype=dtype) | |
| self.pool_v = torch.zeros_like(self.pool_k) | |
| self.stage_k = torch.zeros(L, 1, chunk_tokens, heads, head_dim, device=device, dtype=dtype) | |
| self.stage_v = torch.zeros_like(self.stage_k) | |
| self.pub_k = torch.zeros(2, L, 1, chunk_tokens, heads, head_dim, device=device, dtype=dtype) | |
| self.pub_v = torch.zeros_like(self.pub_k) | |
| self._pub_parity = 0 | |
| cos, sin = pos_freqs | |
| ct = chunk_tokens | |
| # Rope table for cached-KV positions 0..mti-1: row p = freqs of one chunk at temporal position p. | |
| self.pos_cos = cos.squeeze(0).view(self.max_temporal_ids, ct, -1).contiguous() | |
| self.pos_sin = sin.squeeze(0).view(self.max_temporal_ids, ct, -1).contiguous() | |
| # Writable buffer read by run_commit inside the graph; row is swapped per chunk via set_positions. | |
| self.commit_cos = self.pos_cos[self.max_temporal_ids - 1].clone().unsqueeze(0) | |
| self.commit_sin = self.pos_sin[self.max_temporal_ids - 1].clone().unsqueeze(0) | |
| self.in_latent = torch.zeros(latent_shape, device=device, dtype=dtype) | |
| self.in_timestep = torch.zeros((latent_shape[0],), device=device, dtype=torch.float32) | |
| self.in_ref_latent = torch.zeros(latent_shape, device=device, dtype=dtype) | |
| self.in_store_latent = torch.zeros(latent_shape, device=device, dtype=dtype) | |
| self.in_store_timestep = torch.zeros((latent_shape[0],), device=device, dtype=dtype) | |
| self.in_prompt = torch.zeros((1, self.txt_len, int(transformer.config.text_states_dim)), | |
| device=device, dtype=dtype) | |
| self.in_current_ids = torch.full((1, latent_shape[2]), self.max_temporal_ids, | |
| device=device, dtype=torch.long) | |
| self.full_graph: Optional[torch.cuda.CUDAGraph] = None | |
| self.in_noise = torch.zeros(latent_shape, device=device, dtype=dtype) | |
| self.out_latents: Optional[torch.Tensor] = None | |
| self.pool_dirty = True | |
| self.ready = False | |
| self.bound_token: Optional[int] = None | |
| self.last_graph_chunk = -(10 ** 9) | |
| def static_bytes(self) -> int: | |
| """Bytes held by the graph's static buffers (KV pool, staging, I/O).""" | |
| tensors = [ | |
| self.pool_k, self.pool_v, self.stage_k, self.stage_v, self.pub_k, self.pub_v, | |
| self.in_latent, self.in_timestep, self.in_ref_latent, self.in_store_latent, | |
| self.in_store_timestep, self.in_prompt, self.in_current_ids, self.in_noise, | |
| self.out_latents, self.pos_cos, self.pos_sin, self.commit_cos, self.commit_sin, | |
| ] | |
| for cache in self.text_kv_by_step: | |
| for key, value in cache.values(): | |
| tensors.extend((key, value)) | |
| return sum(t.numel() * t.element_size() for t in tensors if isinstance(t, torch.Tensor)) | |
| def _pool_assembler(self, layer_idx, *, device, dtype, cached_freqs_cis=None): | |
| return self.pool_k[layer_idx], self.pool_v[layer_idx] | |
| def _stage_writer(self, layer_idx, key, value): | |
| self.stage_k[layer_idx].copy_(key) | |
| self.stage_v[layer_idx].copy_(value) | |
| def capture(self, *, timesteps: torch.Tensor, sigmas: torch.Tensor) -> None: | |
| tf = self.transformer | |
| t0 = time.time() | |
| mem_pool = torch.cuda.graph_pool_handle() | |
| torch.cuda.synchronize(self.device) | |
| def run_denoise(): | |
| with self.autocast_ctx(): | |
| with tf.cache_context("cond"): | |
| return tf( | |
| hidden_states=self.in_latent, | |
| timestep=self.in_timestep, | |
| encoder_hidden_states=self.in_prompt, | |
| ref_video_latent=self.in_ref_latent, | |
| current_temporal_ids=self.in_current_ids, | |
| cached_temporal_ids=None, | |
| kv_cache_mode="reuse", | |
| kv_cache_scope="cond", | |
| kv_cache_chunk_id=None, | |
| kv_cache_selected_chunk_ids=[], | |
| kv_cache_pre_rope=True, | |
| skip_text_stream=self.text_kv_enabled, | |
| )[0] | |
| def run_store(): | |
| with self.autocast_ctx(): | |
| with tf.cache_context("cond"): | |
| tf( | |
| hidden_states=self.in_store_latent, | |
| timestep=self.in_store_timestep, | |
| encoder_hidden_states=self.in_prompt, | |
| current_temporal_ids=self.in_current_ids, | |
| cached_temporal_ids=None, | |
| kv_cache_mode="store", | |
| kv_cache_scope="cond", | |
| kv_cache_chunk_id=None, | |
| kv_cache_selected_chunk_ids=[], | |
| kv_cache_pre_rope=True, | |
| skip_text_stream=True, | |
| ) | |
| def run_commit(): | |
| seg = slice(self.prev1_off, self.prev1_off + self.chunk_tokens) | |
| for li in range(self.num_layers): | |
| roped = apply_rotary_emb(self.stage_k[li], (self.commit_cos, self.commit_sin)) | |
| self.pool_k[li, :, seg].copy_(roped) | |
| self.pool_v[li, :, seg].copy_(self.stage_v[li]) | |
| ts_vals = [float(t) for t in timesteps] | |
| dt_vals = [float(sigmas[i + 1] - sigmas[i]) for i in range(len(ts_vals))] | |
| def run_full(): | |
| lat32 = self.in_noise.to(torch.float32) | |
| tf._graph_kv_assembler = self._pool_assembler | |
| tf._graph_kv_writer = None | |
| for step_i, (t_val, dt) in enumerate(zip(ts_vals, dt_vals)): | |
| self.in_latent.copy_(lat32.to(self.dtype)) | |
| self.in_timestep.fill_(t_val) | |
| set_text_kv_active( | |
| self.text_kv_by_step[step_i] if self.text_kv_enabled else None | |
| ) | |
| pred = run_denoise() | |
| lat32 = lat32 + pred.to(torch.float32) * dt | |
| self.in_store_latent.copy_(lat32.to(self.dtype)) | |
| tf._graph_kv_assembler = None | |
| tf._graph_kv_writer = self._stage_writer | |
| # The store pass skips the text stream and must not see the cache. | |
| set_text_kv_active(None) | |
| try: | |
| run_store() | |
| finally: | |
| tf._graph_kv_writer = None | |
| run_commit() | |
| return lat32 | |
| try: | |
| run_full() | |
| torch.cuda.synchronize(self.device) | |
| g = torch.cuda.CUDAGraph() | |
| with torch.cuda.graph(g, pool=mem_pool, capture_error_mode="thread_local"): | |
| self.out_latents = run_full() | |
| self.full_graph = g | |
| finally: | |
| tf._graph_kv_assembler = None | |
| tf._graph_kv_writer = None | |
| set_text_kv_active(None) | |
| torch.cuda.synchronize(self.device) | |
| self.ready = True | |
| static_gib = sum(t.numel() * t.element_size() for t in ( | |
| self.pool_k, self.pool_v, self.stage_k, self.stage_v, self.pub_k, self.pub_v)) / 2 ** 30 | |
| print(f"#####[GRAPH] captured whole-chunk graph in {time.time() - t0:.1f}s " | |
| f"(static bufs {static_gib:.2f} GiB, txt_len={self.txt_len}, " | |
| f"ref_tokens={self.ref_tokens}, mti={self.max_temporal_ids})", flush=True) | |
| def bind_session( | |
| self, | |
| token: int, | |
| prompt_embeds: torch.Tensor, | |
| text_kv_by_step: Optional[list[dict[int, tuple[torch.Tensor, torch.Tensor]]]] = None, | |
| ) -> None: | |
| self.in_prompt.copy_(prompt_embeds.to(dtype=self.dtype)) | |
| for dst_cache, src_cache in zip(self.text_kv_by_step, text_kv_by_step or ()): | |
| for layer_idx, (key, value) in src_cache.items(): | |
| dst = dst_cache.get(layer_idx) | |
| if dst is None: | |
| continue | |
| dst[0].copy_(key.to(device=self.device, dtype=self.dtype)) | |
| dst[1].copy_(value.to(device=self.device, dtype=self.dtype)) | |
| self.bound_token = token | |
| self.pool_dirty = True | |
| self.last_graph_chunk = -(10 ** 9) | |
| def _pos_freqs(self, pos: int) -> tuple[torch.Tensor, torch.Tensor]: | |
| return self.pos_cos[pos].unsqueeze(0), self.pos_sin[pos].unsqueeze(0) | |
| def set_positions(self, chunk_idx: int) -> None: | |
| """Point the graph's position-dependent buffers at chunk_idx's temporal layout. | |
| Mirrors _gather_window_temporal_ids for the window [0, k-1, k]: | |
| current sits at min(k, mti), the committed KV (prev1 of chunk k+1) at min(k, mti-1). | |
| """ | |
| mti = self.max_temporal_ids | |
| self.in_current_ids.fill_(min(chunk_idx, mti)) | |
| commit_pos = min(chunk_idx, mti - 1) | |
| self.commit_cos.copy_(self.pos_cos[commit_pos].unsqueeze(0)) | |
| self.commit_sin.copy_(self.pos_sin[commit_pos].unsqueeze(0)) | |
| def _seed_segment(self, chunk_store: dict, off: int, length: int, | |
| freqs: Optional[tuple[torch.Tensor, torch.Tensor]]) -> None: | |
| for li in range(self.num_layers): | |
| entry = chunk_store[li] | |
| k, v = kv_entry_tensors(entry, device=self.device, dtype=self.dtype) | |
| if entry.get("pre_rope", False): | |
| if freqs is None: | |
| raise RuntimeError("graph seed: pre-rope entry in a no-rope segment") | |
| k = apply_rotary_emb(k, freqs) | |
| self.pool_k[li, :, off:off + length].copy_(k) | |
| self.pool_v[li, :, off:off + length].copy_(v) | |
| def seed_from_cache(self, scope_store: dict, *, sink_mem_id: int, | |
| prev1_mem_id: int, prev1_pos: int, ref_mem_id: Optional[int]) -> None: | |
| for mem_id, off, freqs in ( | |
| (sink_mem_id, self.sink_off, self._pos_freqs(0)), | |
| (prev1_mem_id, self.prev1_off, self._pos_freqs(prev1_pos)), | |
| ): | |
| store = scope_store.get(mem_id) | |
| if store is None: | |
| raise RuntimeError(f"graph seed: python cache missing mem_id={mem_id}") | |
| self._seed_segment(store, off, self.chunk_tokens, freqs) | |
| if self.ref_tokens > 0: | |
| store = scope_store.get(ref_mem_id) | |
| if store is None: | |
| raise RuntimeError(f"graph seed: python cache missing ref mem_id={ref_mem_id}") | |
| self._seed_segment(store, self.ref_off, self.ref_tokens, None) | |
| self.pool_dirty = False | |
| def publish_to_cache(self, scope_store: dict, mem_id: int) -> None: | |
| p = self._pub_parity | |
| self._pub_parity ^= 1 | |
| self.pub_k[p].copy_(self.stage_k) | |
| self.pub_v[p].copy_(self.stage_v) | |
| chunk_store = {} | |
| for li in range(self.num_layers): | |
| chunk_store[li] = kv_entry_from_tensors( | |
| self.pub_k[p, li], self.pub_v[p, li], pre_rope=True | |
| ) | |
| scope_store[mem_id] = chunk_store | |