Files
JasonHoku-ComfyUI-Ultimate-…/tests/test_latent_geometry.py
T
Jason HokuandClaude Fable 5.1 e17021ee8c fix(moss): empty latent follows the model's latent ratio; moss_image CLIP type; Skip Layer Guidance option
MOSS-Image (Flux.2 latent: 128ch, 16 px per cell) jobs were built with the
SD-era `h // 8`, so every image sampled a latent with 4x the cells and decoded
at 2x the requested size (832x1216 -> 1664x2432): ~10x slower than the official
ComfyUI template (~120 s vs ~10 s per image on the 5060 Ti).

- model_loader: get_latent_downscale_ratio() reads latent_format.spacial_downscale_ratio
  (8 for SD/SDXL/Flux.1, 16 for the Flux.2 family); make_empty_latent() builds the
  job latent and warns when WxH is not a multiple of the ratio. Both orchestrator
  latent sites (main loop + distributed worker) use it; hires-fix latent upscale,
  tiled hires sampling and VAE tiling divisors no longer assume 8.
- CLIP type: resolve_clip_type() warns on unknown types instead of silently using
  stable_diffusion, and when clip_type is left at the default it follows the
  detected model family (MOSSImage -> moss_image, Flux2 -> flux2, ...). MOSS loaded
  as a generic Qwen3.5 TE before (wrong chat template). The Builder's CLIP Type
  list is now read from ComfyUI's CLIPLoader/DualCLIPLoader (static list = fallback,
  now including moss_image); LS_CACHE_VERSION 3 refreshes cached lists.
- Skip Layer Guidance: new Extra Model & Sampling sub-group wrapping
  SkipLayerGuidanceDiTSimple (double/single layers, start/end %), plumbed like Deep
  Shrink (builder emission, expand_configs, orchestrator, generate_image). Defaults
  match the official MOSS-Image workflow (single block 22, 0-50%).
- tests: latent geometry + clip type resolution, dynamic clip type lists, SLG
  config emission/expansion (24 new; the 15 pre-existing torch-stub failures unchanged).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 20:23:10 -08:00

153 lines
6.2 KiB
Python

"""Latent geometry follows the model's latent format, not a hardcoded 8.
REGRESSION (2026-11): MOSS-Image uses the Flux.2 latent format (128 channels,
16 pixels per latent cell). The orchestrator built every empty latent as
``h // 8, w // 8``, so a 832x1216 job sampled a latent with 4x the cells
(the DiT ran on 4x the tokens) and decoded to 1664x2432 -- about 10x slower
than the official ComfyUI template and twice the requested size.
"""
import enum
import sys
import os
import types
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import importlib.util
# tests/conftest.py registers a lightweight `model_loader` STUB (for the
# orchestrator's relative imports), so load the real module file here under a
# private name, the same way conftest loads remote_vae / distribution / civitai.
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_PKG = os.path.basename(_ROOT)
if not hasattr(sys.modules.get(f"{_PKG}.lora_utils", object()), "LoRAFileNotFoundError"):
# the lora_utils stub lacks the exception class model_loader imports
setattr(sys.modules[f"{_PKG}.lora_utils"], "LoRAFileNotFoundError", type("LoRAFileNotFoundError", (Exception,), {}))
if "comfy" in sys.modules and "comfy.sd" in sys.modules:
sys.modules["comfy"].sd = sys.modules["comfy.sd"] # `import comfy.sd` on stubs does not wire the attribute
_spec = importlib.util.spec_from_file_location(f"{_PKG}.model_loader_real", os.path.join(_ROOT, "model_loader.py"))
model_loader = importlib.util.module_from_spec(_spec)
model_loader.__package__ = _PKG
_spec.loader.exec_module(model_loader)
get_latent_downscale_ratio = model_loader.get_latent_downscale_ratio
empty_latent_shape = model_loader.empty_latent_shape
resolve_clip_type = model_loader.resolve_clip_type
# ---- fakes -----------------------------------------------------------------
def _model(ratio=None, via_config=False, config_name="SDXL", no_inner=False):
"""A fake ModelPatcher: .model.latent_format or .model.model_config.latent_format."""
latent_format = types.SimpleNamespace(latent_channels=128 if ratio == 16 else 4)
if ratio is not None:
latent_format.spacial_downscale_ratio = ratio
model_config = type(config_name, (), {})()
if via_config:
model_config.latent_format = latent_format
inner = types.SimpleNamespace(model_config=model_config)
else:
inner = types.SimpleNamespace(latent_format=latent_format, model_config=model_config)
if no_inner:
return types.SimpleNamespace()
return types.SimpleNamespace(model=inner)
class FakeCLIPType(enum.Enum):
STABLE_DIFFUSION = 1
FLUX = 6
FLUX2 = 25
KREA2 = 30
MOSS_IMAGE = 38
# ---- downscale ratio ---------------------------------------------------------
def test_ratio_from_latent_format_flux2_family():
assert get_latent_downscale_ratio(_model(ratio=16)) == 16
def test_ratio_from_model_config_latent_format():
assert get_latent_downscale_ratio(_model(ratio=16, via_config=True)) == 16
def test_ratio_sd_family_is_8():
assert get_latent_downscale_ratio(_model(ratio=8)) == 8
def test_ratio_defaults_to_8_when_format_has_no_attribute():
# LatentFormat subclasses that never set the attribute (older ComfyUI)
assert get_latent_downscale_ratio(_model(ratio=None)) == 8
def test_ratio_defaults_to_8_for_unusable_model():
assert get_latent_downscale_ratio(_model(no_inner=True)) == 8
assert get_latent_downscale_ratio(None) == 8
# ---- empty latent shape -------------------------------------------------------
def test_empty_latent_shape_moss_832x1216():
# The bug: this used to be [1, 128, 152, 104] (4x the cells)
assert empty_latent_shape(128, 832, 1216, 16) == [1, 128, 76, 52]
def test_empty_latent_shape_sdxl_unchanged():
assert empty_latent_shape(4, 1024, 1024, 8) == [1, 4, 128, 128]
def test_empty_latent_shape_floors_like_comfy():
assert empty_latent_shape(128, 1000, 1000, 16) == [1, 128, 62, 62]
def test_empty_latent_shape_batch_and_default_ratio():
assert empty_latent_shape(4, 512, 768, batch_size=2) == [2, 4, 96, 64]
assert empty_latent_shape(4, 512, 768, downscale_ratio=None) == [1, 4, 96, 64]
# ---- clip type resolution -----------------------------------------------------
def _patch_cliptype(monkeypatch):
monkeypatch.setattr(model_loader.comfy.sd, "CLIPType", FakeCLIPType, raising=False)
def test_unknown_clip_type_falls_back_with_warning(monkeypatch, capsys):
_patch_cliptype(monkeypatch)
member, name = resolve_clip_type("bogus_type")
assert member is FakeCLIPType.STABLE_DIFFUSION and name == "stable_diffusion"
assert "Unknown clip_type 'bogus_type'" in capsys.readouterr().out
def test_explicit_clip_type_is_used_as_is(monkeypatch):
_patch_cliptype(monkeypatch)
member, name = resolve_clip_type("moss_image", _model(ratio=16, config_name="SDXL"))
assert member is FakeCLIPType.MOSS_IMAGE and name == "moss_image"
def test_default_clip_type_follows_model_family(monkeypatch, capsys):
"""MOSS-Image loaded with stable_diffusion got the generic Qwen3.5 encoder
(wrong chat template). The default now follows the detected model config."""
_patch_cliptype(monkeypatch)
member, name = resolve_clip_type("stable_diffusion", _model(ratio=16, config_name="MOSSImage"))
assert member is FakeCLIPType.MOSS_IMAGE and name == "moss_image"
assert "MOSSImage" in capsys.readouterr().out
def test_default_clip_type_stays_for_sd_models(monkeypatch):
_patch_cliptype(monkeypatch)
for family in ("SDXL", "SD15", "ZImage"):
member, name = resolve_clip_type("stable_diffusion", _model(ratio=8, config_name=family))
assert member is FakeCLIPType.STABLE_DIFFUSION and name == "stable_diffusion"
def test_explicit_non_default_wins_over_model_family(monkeypatch):
_patch_cliptype(monkeypatch)
member, name = resolve_clip_type("flux2", _model(ratio=16, config_name="MOSSImage"))
assert member is FakeCLIPType.FLUX2 and name == "flux2"
def test_empty_clip_type_means_default(monkeypatch):
_patch_cliptype(monkeypatch)
assert resolve_clip_type("")[1] == "stable_diffusion"
assert resolve_clip_type(None)[1] == "stable_diffusion"
assert resolve_clip_type(None, _model(ratio=16, config_name="Krea2"))[1] == "krea2"