Files
JasonHoku-ComfyUI-Ultimate-…/tests/test_slg_config.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

118 lines
3.6 KiB
Python

"""Skip Layer Guidance (SkipLayerGuidanceDiTSimple) config plumbing.
Builder UI state -> state_to_configs_json -> expand_configs must carry the
SLG fields the same way Deep Shrink's travel: a boolean toggle that is always
emitted, detail fields only when the toggle is on, and per-config passthrough
in the expanded configs the orchestrator hands to generate_image.
"""
import json
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config_builder_node import UltimateConfigBuilder
from config_utils import expand_configs
def _state(extra_array_fields=None):
arr = {
"name": "Test",
"samplers": ["euler"],
"schedulers": ["normal"],
"steps": "20",
"cfg": "7.0",
"models": ["None"],
"vaes": ["None"],
"text_encoders": [],
"clip_type": "stable_diffusion",
"gguf_options": {},
"loras": ["None"],
"lora_omit_triggers": [],
"lora_triggerwords_append_settings": {},
"lora_bypass_states": {},
"lora_strength_lock": {},
"lora_weight_arrays": {},
"model_bypass_states": {},
"vae_bypass_states": {},
"te_bypass_states": {},
"combine": False,
"positive_prompt_groups": [],
"negative_prompt": "",
"use_custom_prompts": False,
"model_prompt_prefix": "",
"model_prompt_suffix": "",
"attention_modes": ["default"],
}
if extra_array_fields:
arr.update(extra_array_fields)
return {
"session_name": "test_session",
"include_none": False,
"global_positive_groups": [],
"global_negative": "",
"config_arrays": [arr],
}
def _configs(state):
return json.loads(UltimateConfigBuilder.state_to_configs_json(state))["configs"]
def test_slg_off_emits_only_the_toggle():
configs = _configs(_state())
assert len(configs) == 1
c = configs[0]
assert c["use_slg"] is False
assert "slg_single_layers" not in c and "slg_end_percent" not in c
def test_slg_on_emits_detail_fields_with_moss_template_defaults():
configs = _configs(_state({"use_slg": True}))
c = configs[0]
assert c["use_slg"] is True
assert c["slg_double_layers"] == ""
assert c["slg_single_layers"] == "22"
assert c["slg_start_percent"] == 0.0
assert c["slg_end_percent"] == 0.5
def test_slg_on_emits_user_values():
configs = _configs(_state({
"use_slg": True, "slg_double_layers": "7, 8, 9", "slg_single_layers": "",
"slg_start_percent": "0.1", "slg_end_percent": "0.3",
}))
c = configs[0]
assert c["slg_double_layers"] == "7, 8, 9"
assert c["slg_single_layers"] == ""
assert c["slg_start_percent"] == 0.1 and c["slg_end_percent"] == 0.3
def _expand(entry):
return expand_configs(
raw_configs=[entry],
pos_prompts=["a cat"],
neg_prompts=[""],
denoise_values=[1.0],
seed=0,
extra_seeds=[],
ckpt_name="ckpt.safetensors",
)
def test_expand_passes_slg_fields_through():
out = _expand({"sampler": "euler", "scheduler": "normal", "steps": 20, "cfg": 7.0,
"model": "Default", "use_slg": True, "slg_single_layers": "22", "slg_end_percent": 0.5})
assert len(out) == 1
c = out[0]
assert c["use_slg"] is True
assert c["slg_single_layers"] == "22"
assert c["slg_double_layers"] == ""
assert c["slg_start_percent"] == 0.0
assert c["slg_end_percent"] == 0.5
def test_expand_defaults_slg_off():
out = _expand({"sampler": "euler", "scheduler": "normal", "steps": 20, "cfg": 7.0, "model": "Default"})
assert out[0]["use_slg"] is False