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>
118 lines
3.6 KiB
Python
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
|