Files
vllm-omni/vllm_omni/diffusion/models/kandinsky6/cache_accel.py
T
5f95115e70 Kandinsky6 (#8537)
Signed-off-by: leffff <levnovitskiy@gmail.com>
Signed-off-by: Artem Borisov <divotionchannel@gmail.com>
Co-authored-by: leffff <levnovitskiy@gmail.com>
Co-authored-by: Artem Borisov <divotionchannel@gmail.com>
2026-10-06 15:55:15 +08:00

200 lines
5.1 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Kandinsky 6 cache adapters.
Generic MagCache / TeaCache hooks assume a single residual stream named
``hidden_states``. K6's fused visual blocks return ``(video, audio)``, so
the denoise loop consults :class:`K6StepCache` instead and reuses the
previous velocity when the schedule says the step can be skipped.
"""
from __future__ import annotations
from dataclasses import dataclass
# Pro geometry ratios from k6_video
# ``k6_pro_125_480_864_mCache_mOffload.yaml`` (cache.magcache.mag_ratios).
K6_PRO_MAG_RATIOS: tuple[float, ...] = (
1.0,
1.0,
0.94526286,
0.94622554,
1.13130749,
1.11243134,
1.02926022,
1.02675114,
1.011996,
1.01235353,
1.03039566,
1.02848639,
1.00101278,
1.00307236,
1.02238594,
1.02108294,
1.0248162,
1.02491556,
0.99771836,
0.99882495,
1.01694329,
1.01367628,
1.01096435,
1.00959183,
1.01537554,
1.01504827,
0.99237553,
0.99345238,
1.02368983,
1.02088727,
1.01396087,
1.01278108,
0.99773024,
0.99927632,
1.01573298,
1.01489232,
1.00129728,
1.00120319,
1.01759963,
1.01691798,
0.99335877,
0.99548372,
1.02925427,
1.02299559,
0.99749375,
0.99682709,
0.99694342,
1.00064403,
1.01101129,
1.0117984,
1.04420642,
1.03624643,
0.9599572,
0.97176547,
1.00452036,
1.0046107,
1.01447715,
1.01351055,
0.9999695,
1.00270806,
1.00770242,
1.00271775,
0.98795461,
0.98880837,
1.01113352,
1.00860993,
1.00116034,
1.00251894,
1.05147006,
1.04040677,
0.99651072,
0.99212138,
0.99040673,
0.98881456,
1.01265486,
1.01241698,
0.97343865,
0.97302804,
1.02539269,
1.02113221,
1.02117306,
1.02987275,
0.98607016,
0.98262938,
0.98404577,
0.98453141,
1.01065119,
1.00807871,
0.99986077,
0.99668861,
0.98597074,
0.98133325,
0.98239303,
0.98370113,
0.93155548,
0.93534231,
0.94231988,
0.93388087,
0.84200656,
0.83657581,
)
# Uncalibrated. Borrowed from the Qwen-Image TeaCache polynomial so the flag
# skips steps instead of raising. Quality will not match a fitted K6 curve.
K6_TEACACHE_COEFFICIENTS: tuple[float, ...] = (
-4.50000000e02,
2.80000000e02,
-4.50000000e01,
3.20000000e00,
-2.00000000e-02,
)
@dataclass
class K6StepCache:
"""Step-level skip state shared by MagCache and TeaCache."""
kind: str
threshold: float
max_skip_steps: int
retention_ratio: float
mag_ratios: tuple[float, ...] | None = None
coefficients: tuple[float, ...] | None = None
rel_l1_thresh: float = 0.2
last_cond: object = None
last_uncond: object = None
accumulated_err: float = 0.0
accumulated_ratio: float = 1.0
accumulated_steps: int = 0
def should_skip(self, step_index: int) -> bool:
if self.last_cond is None or step_index <= 0:
return False
if self.kind == "mag":
ratios = self.mag_ratios or ()
scale = float(ratios[step_index]) if step_index < len(ratios) else 1.0
self.accumulated_ratio *= scale
self.accumulated_steps += 1
self.accumulated_err += abs(1.0 - self.accumulated_ratio)
if (
self.accumulated_err <= self.threshold
and self.accumulated_steps <= self.max_skip_steps
and step_index >= max(1, int(self.retention_ratio * max(step_index, 1)))
):
return True
self.accumulated_ratio = 1.0
self.accumulated_steps = 0
self.accumulated_err = 0.0
return False
# TeaCache stand-in: uncalibrated polynomial of the step index.
# The real TeaCache distance is the modulated input; this only makes
# the flag skip steps instead of raising.
coeff = self.coefficients or K6_TEACACHE_COEFFICIENTS
x = float(step_index)
predicted = sum(weight * (x ** (len(coeff) - 1 - i)) for i, weight in enumerate(coeff))
return abs(predicted) < self.rel_l1_thresh * 1.0e4
def store(self, cond: object, uncond: object) -> None:
self.last_cond = cond
self.last_uncond = uncond
def attach_mag_cache(transformer, *, threshold: float, max_skip_steps: int, retention_ratio: float) -> None:
transformer._k6_step_cache = K6StepCache(
kind="mag",
threshold=threshold,
max_skip_steps=max_skip_steps,
retention_ratio=retention_ratio,
mag_ratios=K6_PRO_MAG_RATIOS,
)
def attach_tea_cache(transformer, *, rel_l1_thresh: float, coefficients: tuple[float, ...] | None) -> None:
transformer._k6_step_cache = K6StepCache(
kind="tea",
threshold=rel_l1_thresh if rel_l1_thresh is not None else 0.2,
max_skip_steps=3,
retention_ratio=0.0,
coefficients=coefficients or K6_TEACACHE_COEFFICIENTS,
rel_l1_thresh=rel_l1_thresh if rel_l1_thresh is not None else 0.2,
)