Files
automatic/test/test-native-transformer.py
2026-08-10 12:13:10 +03:00

2423 lines
98 KiB
Python

#!/usr/bin/env python
"""
Offline unit tests for pipelines.native_transformer.
Covers the pure helpers that own per-arch knob handling:
- ``drop_companion_keys`` for filtering bundled TE/VAE families out of all-in-one files
- ``strip_prefix`` for single/multi prefix detection and mixed-prefix rejection
- ``partition_siblings`` for inline-sibling key partitioning
- ``check_forbidden_markers`` for structural-mismatch rejection
- ``detect_comfy_quant`` for comfy_quant marker detection and format gating
- ``remap_comfy_quant`` for comfy_quant -> SDNQ key translation
- ``is_noop_converter`` for diffusers no-op lambda detection
- ``validate_state_dict_load`` for unexpected / missing key handling
- ``make_default_spec`` default-spec synthesis with diffusers converter pickup
- ``auto_pickup_converter`` for diffusers ``SINGLE_FILE_LOADABLE_CLASSES`` integration
- ``TransformerSpec`` / ``SiblingSpec`` defaults
Plus one end-to-end ``load`` test against a tiny mock module that exercises
the read -> strip -> convert -> from_config -> load_state_dict -> validate
pipeline without needing a real diffusers transformer or hf_hub_download.
No running server required.
Usage:
python test/test-native-transformer.py
"""
import os
import sys
import tempfile
import torch
import safetensors.torch
script_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, script_dir)
os.chdir(script_dir)
os.environ['SD_INSTALL_QUIET'] = '1'
# Bootstrap cmd_args before any module that pulls in shared.py.
import modules.cmd_args # pylint: disable=wrong-import-position
import installer # pylint: disable=wrong-import-position
_orig_argv = sys.argv
sys.argv = [sys.argv[0]]
try:
modules.cmd_args.parse_args()
finally:
sys.argv = _orig_argv
installer.add_args(modules.cmd_args.parser)
modules.cmd_args.parsed, _ = modules.cmd_args.parser.parse_known_args([])
from modules.errors import log # pylint: disable=wrong-import-position
from pipelines import native_transformer as nt # pylint: disable=wrong-import-position
# ============================================================
# Test infrastructure
# ============================================================
results: dict[str, dict] = {}
def category(name: str):
if name not in results:
results[name] = {'passed': 0, 'failed': 0, 'tests': []}
return name
def record(cat: str, passed: bool, name: str, detail: str = ''):
status = 'PASS' if passed else 'FAIL'
results[cat]['passed' if passed else 'failed'] += 1
results[cat]['tests'].append((status, name))
msg = f' {status}: {name}'
if detail:
msg += f' ({detail})'
if passed:
log.info(msg)
else:
log.error(msg)
def run_test(cat: str, fn):
name = fn.__name__
try:
ok = fn()
if ok is False:
record(cat, False, name)
else:
record(cat, True, name)
except AssertionError as e:
record(cat, False, name, str(e))
except Exception as e: # pylint: disable=broad-except
record(cat, False, name, f'exception: {e}')
import traceback
traceback.print_exc()
# ============================================================
# drop_companion_keys
# ============================================================
def test_drop_companion_keys_no_companions_pass_through():
sd = {'net.blocks.0.weight': 1, 'net.blocks.1.weight': 2}
out = nt.drop_companion_keys(sd, nt.DEFAULT_IGNORED_PREFIXES, 'Test')
assert out == sd, 'files without companion keys must pass through unchanged'
def test_drop_companion_keys_filters_all_in_one_layout():
sd = {
'net.blocks.0.weight': 1,
'net.llm_adapter.proj.weight': 2,
'cond_stage_model.qwen3_06b.transformer.model.embed_tokens.weight': 3,
'first_stage_model.decoder.conv1.weight': 4,
'vae.decoder.conv_in.weight': 5,
'text_encoders.clip_l.weight': 6,
}
out = nt.drop_companion_keys(sd, nt.DEFAULT_IGNORED_PREFIXES, 'Test')
assert set(out.keys()) == {'net.blocks.0.weight', 'net.llm_adapter.proj.weight'}
def test_drop_companion_keys_raises_when_nothing_left():
sd = {
'cond_stage_model.te.weight': 1,
'first_stage_model.decoder.weight': 2,
}
try:
nt.drop_companion_keys(sd, nt.DEFAULT_IGNORED_PREFIXES, 'Test')
raise AssertionError('expected ValueError')
except ValueError as e:
assert 'no transformer keys' in str(e)
def test_drop_companion_keys_empty_prefix_tuple_no_op():
sd = {'cond_stage_model.te.weight': 1}
out = nt.drop_companion_keys(sd, (), 'Test')
assert out == sd, 'empty ignored_prefixes must disable filtering'
def test_drop_then_strip_all_in_one_layout():
"""Companion filtering must clear the way for normal prefix detection."""
sd = {
'net.blocks.0.weight': 1,
'net.final_layer.weight': 2,
'cond_stage_model.te.weight': 3,
'first_stage_model.decoder.weight': 4,
}
out = nt.drop_companion_keys(sd, nt.DEFAULT_IGNORED_PREFIXES, 'Test')
out, prefix = nt.strip_prefix(out, nt.DEFAULT_PREFIXES, 'Test')
assert prefix == 'net.'
assert set(out.keys()) == {'blocks.0.weight', 'final_layer.weight'}
# ============================================================
# strip_prefix
# ============================================================
def test_strip_prefix_bare_keys_pass_through():
sd = {'layers.0.weight': 1, 'layers.0.bias': 2}
out, prefix = nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
assert out == sd, 'bare keys must pass through unchanged'
assert prefix == '', 'bare keys must report an empty prefix'
def test_strip_prefix_dominant_single_variant():
sd = {f'model.diffusion_model.layers.{i}.weight': i for i in range(10)}
out, prefix = nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
assert all(k.startswith('layers.') for k in out)
assert len(out) == 10
assert prefix == 'model.diffusion_model.'
def test_strip_prefix_picks_longest_match_first():
"""``model.diffusion_model.`` must beat ``diffusion_model.`` when both match."""
sd = {
'model.diffusion_model.layers.0.weight': 1,
'model.diffusion_model.layers.1.weight': 2,
}
out, prefix = nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
# If shorter prefix matched, keys would start with 'model.'
assert 'layers.0.weight' in out
assert 'layers.1.weight' in out
assert not any(k.startswith('model.') for k in out)
assert prefix == 'model.diffusion_model.'
def test_strip_prefix_mixed_prefixes_raises():
sd = {
'model.diffusion_model.layers.0.weight': 1,
'net.layers.0.weight': 2,
}
try:
nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
raise AssertionError('expected ValueError')
except ValueError as e:
assert 'mixed prefixes' in str(e)
def test_strip_prefix_net_variant():
sd = {'net.layers.0.weight': 1, 'net.layers.1.bias': 2}
out, prefix = nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
assert set(out.keys()) == {'layers.0.weight', 'layers.1.bias'}
assert prefix == 'net.'
def test_strip_prefix_diffusion_model_variant():
sd = {'diffusion_model.layers.0.weight': 1, 'diffusion_model.layers.1.bias': 2}
out, prefix = nt.strip_prefix(sd, nt.DEFAULT_PREFIXES, 'Test')
assert set(out.keys()) == {'layers.0.weight', 'layers.1.bias'}
assert prefix == 'diffusion_model.'
def test_strip_prefix_custom_prefix_set():
sd = {'lora_unet_blocks_0.weight': 1, 'lora_unet_blocks_1.weight': 2}
out, prefix = nt.strip_prefix(sd, ('lora_unet_',), 'Test')
assert set(out.keys()) == {'blocks_0.weight', 'blocks_1.weight'}
assert prefix == 'lora_unet_'
# ============================================================
# partition_siblings
# ============================================================
def test_partition_siblings_empty_spec_returns_state_dict_unchanged():
sd = {'a': 1, 'b': 2}
transformer_sd, siblings = nt.partition_siblings(sd, {})
assert transformer_sd == sd
assert siblings == {}
def test_partition_siblings_no_matches_keeps_all_in_transformer():
sd = {'layers.0.weight': 1, 'layers.1.weight': 2}
siblings_spec = {'llm_adapter': nt.SiblingSpec(subfolder='llm_adapter', inline_prefix='llm_adapter.')}
transformer_sd, siblings = nt.partition_siblings(sd, siblings_spec)
assert transformer_sd == sd
assert siblings == {'llm_adapter': {}}
def test_partition_siblings_single_sibling_split():
sd = {
'layers.0.weight': 'tx0',
'layers.1.weight': 'tx1',
'llm_adapter.input_proj.weight': 'ad0',
'llm_adapter.output_proj.weight': 'ad1',
}
siblings_spec = {'llm_adapter': nt.SiblingSpec(subfolder='llm_adapter', inline_prefix='llm_adapter.')}
transformer_sd, siblings = nt.partition_siblings(sd, siblings_spec)
assert set(transformer_sd.keys()) == {'layers.0.weight', 'layers.1.weight'}
assert set(siblings['llm_adapter'].keys()) == {'input_proj.weight', 'output_proj.weight'}
assert siblings['llm_adapter']['input_proj.weight'] == 'ad0'
def test_partition_siblings_multiple_siblings():
sd = {
'layers.0.weight': 'tx',
'sibling_a.x.weight': 'a0',
'sibling_b.y.weight': 'b0',
'sibling_b.z.weight': 'b1',
}
siblings_spec = {
'sibling_a': nt.SiblingSpec(subfolder='a', inline_prefix='sibling_a.'),
'sibling_b': nt.SiblingSpec(subfolder='b', inline_prefix='sibling_b.'),
}
transformer_sd, siblings = nt.partition_siblings(sd, siblings_spec)
assert list(transformer_sd.keys()) == ['layers.0.weight']
assert set(siblings['sibling_a'].keys()) == {'x.weight'}
assert set(siblings['sibling_b'].keys()) == {'y.weight', 'z.weight'}
# ============================================================
# check_forbidden_markers
# ============================================================
def test_forbidden_markers_passes_when_absent():
sd = {'layers.0.weight': 1}
markers = (('legacy.marker.weight', 'old format'),)
nt.check_forbidden_markers(sd, markers, 'Test', '/tmp/x.safetensors')
# no exception = pass
def test_forbidden_markers_raises_when_present():
sd = {'layers.0.weight': 1, 'legacy.marker.weight': 2}
markers = (('legacy.marker.weight', 'old Cosmos 1.0 structure'),)
try:
nt.check_forbidden_markers(sd, markers, 'Test', '/tmp/x.safetensors')
raise AssertionError('expected ValueError')
except ValueError as e:
msg = str(e)
assert 'old Cosmos 1.0 structure' in msg
assert 'legacy.marker.weight' in msg
def test_forbidden_markers_empty_tuple_no_op():
sd = {'layers.0.weight': 1}
nt.check_forbidden_markers(sd, (), 'Test', '/tmp/x.safetensors')
# ============================================================
# detect_comfy_quant / remap_comfy_quant
# ============================================================
def comfy_marker(fmt: str) -> torch.Tensor:
import json
return torch.tensor(list(json.dumps({'format': fmt}).encode()), dtype=torch.uint8)
def test_detect_comfy_no_markers_returns_none():
sd = {'blocks.0.attn.wq.weight': torch.zeros(2), 'blocks.0.attn.wq.bias': torch.zeros(2)}
assert nt.detect_comfy_quant(sd, 'Test') is None
def test_detect_comfy_valid_markers():
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
'blocks.0.attn.wq.comfy_quant': comfy_marker('int8_tensorwise'),
'blocks.0.mlp.up.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.mlp.up.weight_scale': torch.tensor(0.25),
'blocks.0.mlp.up.comfy_quant': comfy_marker('int8_tensorwise'),
'norm.weight': torch.zeros(4),
}
detected = nt.detect_comfy_quant(sd, 'Test')
assert detected is not None
marked, fmt = detected
assert set(marked) == {'blocks.0.attn.wq', 'blocks.0.mlp.up'}
assert marked['blocks.0.attn.wq'] == {'format': 'int8_tensorwise'}
assert fmt == 'int8_tensorwise'
def test_detect_comfy_unsupported_format_raises():
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4)),
'blocks.0.attn.wq.comfy_quant': comfy_marker('float8_e4m3fn_scaled'),
}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'float8_e4m3fn_scaled' in str(e)
def test_detect_comfy_malformed_marker_raises():
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4)),
'blocks.0.attn.wq.comfy_quant': torch.tensor(list(b'not json'), dtype=torch.uint8),
}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'malformed' in str(e)
def test_detect_comfy_convrot_accepted():
"""ConvRot markers map onto SDNQ's Hadamard support; detection carries the
per-layer fields through instead of rejecting."""
import json
payload = json.dumps({'format': 'int8_tensorwise', 'convrot': True, 'convrot_groupsize': 256, 'per_row': True})
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.comfy_quant': torch.tensor(list(payload.encode()), dtype=torch.uint8),
'blocks.0.mlp.up.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.mlp.up.comfy_quant': comfy_marker('int8_tensorwise'),
}
marked, fmt = nt.detect_comfy_quant(sd, 'Test')
assert fmt == 'int8_tensorwise'
assert marked['blocks.0.attn.wq'].get('convrot') is True
assert marked['blocks.0.attn.wq'].get('convrot_groupsize') == 256
assert not marked['blocks.0.mlp.up'].get('convrot')
def test_detect_comfy_convrot_bad_groupsize_raises():
"""Regular Hadamards only exist for power-of-4 sizes; any other convrot
group size cannot be a compatible rotation."""
import json
payload = json.dumps({'format': 'int8_tensorwise', 'convrot': True, 'convrot_groupsize': 8})
sd = {
'blocks.0.attn.wq.comfy_quant': torch.tensor(list(payload.encode()), dtype=torch.uint8),
}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'power of 4' in str(e)
def test_detect_comfy_marker_missing_format_field_raises():
import json
sd = {
'blocks.0.attn.wq.comfy_quant': torch.tensor(list(json.dumps({'fmt': 'x'}).encode()), dtype=torch.uint8),
}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'malformed' in str(e)
def test_remap_comfy_renames_and_reshapes_scale():
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
'blocks.0.attn.wq.comfy_quant': comfy_marker('int8_tensorwise'),
}
out = nt.remap_comfy_quant(sd, {'blocks.0.attn.wq'})
assert set(out.keys()) == {'blocks.0.attn.wq.weight', 'blocks.0.attn.wq.scale'}
assert out['blocks.0.attn.wq.scale'].shape == (1, 1)
assert out['blocks.0.attn.wq.scale'].item() == 0.5
assert out['blocks.0.attn.wq.weight'].dtype == torch.int8
def test_remap_comfy_passes_unmarked_keys_verbatim():
unrelated_scale = torch.tensor(2.0)
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
'blocks.0.attn.wq.comfy_quant': comfy_marker('int8_tensorwise'),
'norm.weight': torch.ones(4),
'other.weight_scale': unrelated_scale,
}
out = nt.remap_comfy_quant(sd, {'blocks.0.attn.wq'})
assert 'norm.weight' in out
assert out['other.weight_scale'] is unrelated_scale, 'weight_scale outside marked names must pass through untouched'
def test_remap_comfy_does_not_mutate_input():
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
'blocks.0.attn.wq.comfy_quant': comfy_marker('int8_tensorwise'),
}
keys_before = set(sd.keys())
nt.remap_comfy_quant(sd, {'blocks.0.attn.wq'})
assert set(sd.keys()) == keys_before, 'input dict must not be mutated (read_state_dict caches it)'
def test_remap_comfy_drops_input_scale():
"""Optional activation-calibration sidecars are dropped for marked layers
(SDNQ derives activation scales dynamically); unmarked ones pass through."""
unrelated = torch.tensor(1.0)
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
'blocks.0.attn.wq.input_scale': torch.tensor(0.125),
'blocks.0.attn.wq.comfy_quant': comfy_marker('int8_tensorwise'),
'other.input_scale': unrelated,
}
out = nt.remap_comfy_quant(sd, {'blocks.0.attn.wq'})
assert 'blocks.0.attn.wq.input_scale' not in out
assert out['other.input_scale'] is unrelated
# ============================================================
# header _quantization_metadata
# ============================================================
def quant_metadata(layers: dict) -> dict:
import json
return {'_quantization_metadata': json.dumps({'format_version': '1.0', 'layers': layers})}
def test_read_quant_metadata_absent_returns_none():
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
write_fixture({'a.weight': torch.zeros(2)}, fd, path)
assert nt.read_quantization_metadata(path) is None
finally:
os.unlink(path)
def test_read_quant_metadata_parses_layers():
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
layers = {'blocks.0.attn.wq': {'format': 'int8_tensorwise'}}
write_fixture({'blocks.0.attn.wq.weight': torch.zeros(2)}, fd, path, metadata=quant_metadata(layers))
assert nt.read_quantization_metadata(path) == layers
finally:
os.unlink(path)
def test_read_quant_metadata_malformed_raises():
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
write_fixture({'a.weight': torch.zeros(2)}, fd, path, metadata={'_quantization_metadata': 'not json'})
try:
nt.read_quantization_metadata(path)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'malformed' in str(e)
finally:
os.unlink(path)
def test_read_quant_metadata_non_dict_layers_raises():
import json
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
payload = json.dumps({'layers': {'blocks.0.attn.wq': 'int8_tensorwise'}})
write_fixture({'a.weight': torch.zeros(2)}, fd, path, metadata={'_quantization_metadata': payload})
try:
nt.read_quantization_metadata(path)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'malformed' in str(e)
finally:
os.unlink(path)
def test_transcode_quant_metadata_synthesizes_markers():
"""Header entries become marker tensors for layers whose weight is present;
entries without a matching weight (companion components) are skipped."""
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.weight_scale': torch.tensor(0.5),
}
layers = {
'blocks.0.attn.wq': {'format': 'int8_tensorwise'},
'text_encoder.mlp.up': {'format': 'int8_tensorwise'},
}
out, source = nt.transcode_quant_metadata(sd, layers)
assert source == 'header'
assert 'blocks.0.attn.wq.comfy_quant' in out
assert 'text_encoder.mlp.up.comfy_quant' not in out
assert 'blocks.0.attn.wq.comfy_quant' not in sd, 'input dict must not be mutated'
marked, fmt = nt.detect_comfy_quant(out, 'Test')
assert fmt == 'int8_tensorwise'
assert marked['blocks.0.attn.wq'] == {'format': 'int8_tensorwise'}
def test_transcode_quant_metadata_header_wins():
"""When both forms are present the header entry overwrites the marker,
matching the reference loader's precedence."""
sd = {
'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8),
'blocks.0.attn.wq.comfy_quant': comfy_marker('float8_e4m3fn'),
}
layers = {'blocks.0.attn.wq': {'format': 'int8_tensorwise', 'full_precision_matrix_mult': True}}
out, source = nt.transcode_quant_metadata(sd, layers)
assert source == 'both'
marked, fmt = nt.detect_comfy_quant(out, 'Test')
assert fmt == 'int8_tensorwise'
assert marked['blocks.0.attn.wq'].get('full_precision_matrix_mult') is True
def test_transcode_quant_metadata_no_matches_is_noop():
sd = {'blocks.0.attn.wq.weight': torch.zeros((4, 4), dtype=torch.int8)}
out, source = nt.transcode_quant_metadata(sd, {'unrelated.layer': {'format': 'int8_tensorwise'}})
assert source == 'markers'
assert out is sd
# ============================================================
# nvfp4: sdnq codec, scale unswizzle, detection
# ============================================================
E2M1_VALUES = (0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, -0.0, -0.5, -1.0, -1.5, -2.0, -3.0, -4.0, -6.0)
def to_blocked_reference(matrix: torch.Tensor) -> torch.Tensor:
"""cuBLAS block-scaling tile layout as written into nvfp4 containers;
the loader's unswizzle must invert this exactly."""
rows, cols = matrix.shape
row_blocks = -(rows // -128)
col_blocks = -(cols // -4)
padded = torch.zeros((row_blocks * 128, col_blocks * 4), dtype=matrix.dtype)
padded[:rows, :cols] = matrix
blocks = padded.view(row_blocks, 128, col_blocks, 4).permute(0, 2, 1, 3)
rearranged = blocks.reshape(-1, 4, 32, 4).transpose(1, 2).reshape(-1, 32, 16)
return rearranged.reshape(row_blocks * 128, col_blocks * 4)
def test_unswizzle_block_scales_roundtrip():
for rows, groups in ((128, 4), (8, 3), (200, 10)):
mat = torch.arange(rows * groups, dtype=torch.float32).reshape(rows, groups)
back = nt.unswizzle_block_scales(to_blocked_reference(mat), rows, groups)
assert torch.equal(back, mat), f'unswizzle mismatch for {(rows, groups)}'
def test_nvfp4_codec_ocp_table():
"""SDNQ's float4_e2m1fn decodes OCP FP4 E2M1 exactly, including the
subnormal codes 1/9 as +/-0.5; nvfp4 containers adopt it directly."""
from sdnq.packed_float import unpack_float
packed = torch.tensor([(2 * j) | (((2 * j) + 1) << 4) for j in range(8)], dtype=torch.uint8)
dec = unpack_float(packed, 'float4_e2m1fn', torch.Size([16]))
for code in range(16):
assert float(dec[code]) == E2M1_VALUES[code], f'code {code}: {float(dec[code])} != {E2M1_VALUES[code]}'
def test_nvfp4_pack_ocp_grid_roundtrip():
"""Every OCP grid value survives a pack/unpack round trip exactly
(subnormals included), and off-grid values land inside the value set.
Exact nearest-rounding near the grid midpoints is not asserted: the
packer's staged rounding may resolve boundary values to either side."""
from sdnq.packed_float import pack_float, unpack_float
grid = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, -0.5, -1.0, -1.5, -2.0, -3.0, -4.0, -6.0, 0.0]
vals = torch.tensor(grid, dtype=torch.float32)
out = unpack_float(pack_float(vals, 'float4_e2m1fn'), 'float4_e2m1fn', vals.shape)
assert torch.equal(out, vals), f'grid values must roundtrip exactly: {out.tolist()}'
off_grid = torch.tensor([0.2, 0.6, 0.9, 1.2, 2.4, 5.5, -0.4, -0.9, -1.7, -3.4, -5.9, 0.05, 0.99, -0.99], dtype=torch.float32)
out = unpack_float(pack_float(off_grid, 'float4_e2m1fn'), 'float4_e2m1fn', off_grid.shape)
for v, o in zip(off_grid.tolist(), out.tolist()):
assert abs(o) in {0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, f'{v} -> {o} outside the OCP set'
def test_detect_comfy_nvfp4_marker_accepted():
import json
payload = json.dumps({'format': 'nvfp4', 'group_size': 16, 'orig_dtype': 'torch.float16', 'orig_shape': [32, 32]})
sd = {
'in_proj.weight': torch.zeros((32, 16), dtype=torch.uint8),
'in_proj.comfy_quant': torch.tensor(list(payload.encode()), dtype=torch.uint8),
}
marked, fmt = nt.detect_comfy_quant(sd, 'Test')
assert fmt == 'nvfp4'
assert marked['in_proj']['orig_shape'] == [32, 32]
def test_detect_comfy_nvfp4_bad_group_size_raises():
import json
payload = json.dumps({'format': 'nvfp4', 'group_size': 32})
sd = {'a.comfy_quant': torch.tensor(list(payload.encode()), dtype=torch.uint8)}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'group_size' in str(e)
def test_detect_comfy_nvfp4_convrot_raises():
import json
payload = json.dumps({'format': 'nvfp4', 'convrot': True, 'convrot_groupsize': 16})
sd = {'a.comfy_quant': torch.tensor(list(payload.encode()), dtype=torch.uint8)}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'convrot' in str(e)
# ============================================================
# model_probe quant detection (header-only, mirrors the loader's coverage)
# ============================================================
def test_probe_detects_header_quant_metadata():
import json
from modules import model_probe
header = {
'__metadata__': {'_quantization_metadata': json.dumps({'layers': {'blocks.0.wq': {'format': 'float8_e4m3fn'}}})},
'blocks.0.wq.weight': {'dtype': 'F8_E4M3', 'shape': [4, 4]},
'blocks.0.wq.weight_scale': {'dtype': 'F32', 'shape': []},
}
quant = model_probe.analyze_header(header)['quant']
assert quant == {'scheme': 'comfy_quant', 'format': 'float8_e4m3fn', 'marked_layers': 1, 'source': 'header'}
def test_probe_resolves_nvfp4_from_weight_dtype():
from modules import model_probe
header = {
'model.diffusion_model.blocks.0.wq.weight': {'dtype': 'U8', 'shape': [32, 16]},
'model.diffusion_model.blocks.0.wq.weight_scale': {'dtype': 'F8_E4M3', 'shape': [128, 4]},
'model.diffusion_model.blocks.0.wq.weight_scale_2': {'dtype': 'F32', 'shape': []},
'model.diffusion_model.blocks.0.wq.comfy_quant': {'dtype': 'U8', 'shape': [60]},
}
quant = model_probe.analyze_header(header)['quant']
assert quant['format'] == 'nvfp4'
assert quant['source'] == 'weight-dtype'
def test_probe_fp8_markers_despite_u8_noise():
"""The marked layer's own weight dtype decides the format; unrelated U8
tensors must not outvote it."""
from modules import model_probe
header = {
'blocks.0.wq.weight': {'dtype': 'F8_E4M3', 'shape': [8, 8]},
'blocks.0.wq.weight_scale': {'dtype': 'F32', 'shape': []},
'blocks.0.wq.comfy_quant': {'dtype': 'U8', 'shape': [27]},
'extra.blob1': {'dtype': 'U8', 'shape': [64]},
'extra.blob2': {'dtype': 'U8', 'shape': [64]},
}
quant = model_probe.analyze_header(header)['quant']
assert quant['format'] == 'float8_e4m3fn'
def test_probe_int8_markers():
from modules import model_probe
header = {
'blocks.0.wq.weight': {'dtype': 'I8', 'shape': [8, 8]},
'blocks.0.wq.weight_scale': {'dtype': 'F32', 'shape': []},
'blocks.0.wq.comfy_quant': {'dtype': 'U8', 'shape': [29]},
}
quant = model_probe.analyze_header(header)['quant']
assert quant['format'] == 'int8_tensorwise'
assert quant['marked_layers'] == 1
def test_probe_plain_file_no_quant():
from modules import model_probe
header = {'blocks.0.wq.weight': {'dtype': 'BF16', 'shape': [8, 8]}}
assert model_probe.analyze_header(header)['quant']['scheme'] is None
# ============================================================
# is_noop_converter
# ============================================================
def test_noop_converter_identity_lambda():
fn = lambda checkpoint, **kwargs: checkpoint # pylint: disable=unnecessary-lambda-assignment
assert nt.is_noop_converter(fn) is True
def test_noop_converter_real_function():
def real(checkpoint, **kwargs): # pylint: disable=unused-argument
return {k.replace('a.', 'b.'): v for k, v in checkpoint.items()}
assert nt.is_noop_converter(real) is False
def test_noop_converter_lambda_with_modification():
fn = lambda checkpoint, **kwargs: {k: v.float() for k, v in checkpoint.items()} # pylint: disable=unnecessary-lambda-assignment
assert nt.is_noop_converter(fn) is False
# ============================================================
# validate_state_dict_load
# ============================================================
def test_validate_accepts_buffer_only_missing():
nt.validate_state_dict_load(
component_name='transformer',
missing=['rope.freqs', 'pos_embedder.pos'],
unexpected=[],
acceptable_missing=('rope.', 'pos_embedder.'),
)
def test_validate_rejects_unexpected():
try:
nt.validate_state_dict_load(
component_name='transformer',
missing=[],
unexpected=['some.junk.weight'],
acceptable_missing=(),
)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'unexpected' in str(e)
assert 'some.junk.weight' in str(e)
def test_validate_rejects_hard_missing():
try:
nt.validate_state_dict_load(
component_name='transformer',
missing=['layers.0.weight', 'rope.freqs'],
unexpected=[],
acceptable_missing=('rope.',),
)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
msg = str(e)
assert 'missing' in msg
assert 'layers.0.weight' in msg
# Buffer-only missing must not show up in the hard-missing list
assert msg.count('rope.freqs') == 0
def test_validate_empty_passes():
nt.validate_state_dict_load(
component_name='transformer',
missing=[],
unexpected=[],
acceptable_missing=(),
)
# ============================================================
# make_default_spec
# ============================================================
class FakeTransformer:
"""Minimal stand-in for a diffusers transformer class."""
def test_make_default_spec_for_unknown_class():
spec = nt.make_default_spec(FakeTransformer)
assert spec.cls is FakeTransformer
assert spec.subfolder == 'transformer'
assert spec.prefixes == nt.DEFAULT_PREFIXES
assert spec.converter is None # no diffusers entry for FakeTransformer
assert spec.siblings == {}
assert spec.forbidden_markers == ()
def test_make_default_spec_picks_up_real_diffusers_converter():
import diffusers
spec = nt.make_default_spec(diffusers.FluxTransformer2DModel)
assert spec.converter is not None
assert spec.converter.__name__ == 'convert_flux_transformer_checkpoint_to_diffusers'
def test_make_default_spec_skips_qwen_image_noop():
"""QwenImageTransformer2DModel's diffusers entry is a no-op lambda; the
default spec must NOT pick it up, leaving converter=None so the caller
sees only their own (potentially absent) override."""
import diffusers
spec = nt.make_default_spec(diffusers.QwenImageTransformer2DModel)
assert spec.converter is None
# ============================================================
# auto_pickup_converter
# ============================================================
def test_auto_pickup_returns_none_for_unknown_class():
assert nt.auto_pickup_converter(FakeTransformer) is None
def test_auto_pickup_returns_real_diffusers_converter():
import diffusers
fn = nt.auto_pickup_converter(diffusers.FluxTransformer2DModel)
assert fn is not None
assert callable(fn)
assert fn.__name__ == 'convert_flux_transformer_checkpoint_to_diffusers'
def test_auto_pickup_skips_noop_converter_qwen():
"""QwenImageTransformer2DModel registers a no-op lambda in diffusers;
auto_pickup_converter must return None so the spec falls back to no
converter (the user-registered spec can override with a real converter)."""
import diffusers
assert nt.auto_pickup_converter(diffusers.QwenImageTransformer2DModel) is None
# ============================================================
# TransformerSpec / SiblingSpec defaults
# ============================================================
def test_transformer_spec_defaults():
spec = nt.TransformerSpec(cls=FakeTransformer)
assert spec.subfolder == 'transformer'
assert spec.prefixes == ('model.diffusion_model.', 'diffusion_model.', 'net.')
assert spec.converter is None
assert spec.siblings == {}
assert spec.acceptable_missing == ('rope.', 'pos_embedder.', 'learnable_pos_embed.')
assert spec.ignored_prefixes == ('cond_stage_model.', 'conditioner.', 'first_stage_model.', 'text_encoders.', 'vae.')
assert spec.forbidden_markers == ()
def test_sibling_spec_defaults():
spec = nt.SiblingSpec(subfolder='llm_adapter', inline_prefix='llm_adapter.')
assert spec.subfolder == 'llm_adapter'
assert spec.inline_prefix == 'llm_adapter.'
assert spec.acceptable_missing == ()
def test_transformer_spec_is_frozen():
spec = nt.TransformerSpec(cls=FakeTransformer)
try:
spec.subfolder = 'changed' # type: ignore[misc]
except Exception as e: # pylint: disable=broad-except
assert 'FrozenInstanceError' in type(e).__name__ or 'frozen' in str(e).lower()
return
raise AssertionError('expected FrozenInstanceError')
# ============================================================
# Integration: end-to-end load() with a tiny mock module
# ============================================================
# We sidestep diffusers + hf_hub_download by patching:
# - ``fetch_component_config`` to return a hand-rolled config dict
# - ``model_quant.get_dit_args`` / ``model_quant.get_quant_type`` / quant
# application to no-ops (we only want to test the load path itself).
# The mock cls is a torch.nn.Module subclass whose ``from_config`` constructs
# a fresh module of the expected shape; ``load_state_dict`` is the standard
# PyTorch method.
class MockMiniTransformer(torch.nn.Module):
"""Tiny stand-in: linear in -> linear out, plus a nested rope sub-module
holding a buffer the trainer state dict won't carry. Nested mirrors how
real DiTs structure rope / pos_embedder buffers."""
@classmethod
def from_config(cls, config: dict) -> 'MockMiniTransformer':
return cls(dim=config['dim'])
def __init__(self, dim: int):
super().__init__()
self.in_proj = torch.nn.Linear(dim, dim)
self.out_proj = torch.nn.Linear(dim, dim)
self.rope = torch.nn.Module()
self.rope.register_buffer('freqs', torch.zeros(dim))
class MockKwargsTransformer(MockMiniTransformer):
"""Records the kwargs from_config received, so a test can assert the native
path forwards caller kwargs to construction. Mirrors diffusers from_config,
which accepts **kwargs."""
last_kwargs: dict = {}
@classmethod
def from_config(cls, config: dict, **kwargs) -> 'MockKwargsTransformer':
cls.last_kwargs = dict(kwargs)
return cls(dim=config['dim'])
def write_fixture(state_dict_keys: dict, fd: int, path: str, metadata: dict | None = None) -> str:
os.close(fd)
safetensors.torch.save_file(state_dict_keys, path, metadata=metadata)
return path
def test_load_end_to_end_with_bfl_prefix_no_converter():
"""Exercise the full load pipeline: read .safetensors, strip prefix,
no converter, instantiate via from_config, load weights, validate.
"""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
# Save with model.diffusion_model. prefix; in_proj.* and out_proj.*
# are the real weights the mock cls expects after the strip.
raw = {
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
}
write_fixture(raw, fd, path)
# Patch fetch_component_config to return our hand-rolled config.
orig_fetch = nt.fetch_component_config
nt.fetch_component_config = lambda repo, sub: {'dim': dim}
# Patch quant helpers (we only care about the load path).
from modules import model_quant
orig_get_dit = model_quant.get_dit_args
orig_get_qtype = model_quant.get_quant_type
orig_do_post = model_quant.do_post_load_quant
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
model_quant.do_post_load_quant = lambda *a, **k: None
try:
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, siblings = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
)
finally:
nt.fetch_component_config = orig_fetch
model_quant.get_dit_args = orig_get_dit
model_quant.get_quant_type = orig_get_qtype
model_quant.do_post_load_quant = orig_do_post
assert isinstance(transformer, MockMiniTransformer)
assert transformer.in_proj.weight.shape == (dim, dim)
# Weights from the fixture should match what was loaded.
loaded_in_w = transformer.in_proj.weight.detach().cpu()
fixture_in_w = raw['model.diffusion_model.in_proj.weight'].to(loaded_in_w.dtype)
assert torch.allclose(loaded_in_w, fixture_in_w)
assert siblings == {}
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_forwards_kwargs_to_from_config():
"""Caller **kwargs reach cls.from_config through the native load path
rather than being dropped."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = {
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
}
write_fixture(raw, fd, path)
orig_fetch = nt.fetch_component_config
nt.fetch_component_config = lambda repo, sub: {'dim': dim}
from modules import model_quant
orig_get_dit = model_quant.get_dit_args
orig_get_qtype = model_quant.get_quant_type
orig_do_post = model_quant.do_post_load_quant
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
model_quant.do_post_load_quant = lambda *a, **k: None
MockKwargsTransformer.last_kwargs = {}
try:
spec = nt.TransformerSpec(cls=MockKwargsTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
low_cpu_mem_usage=True,
)
finally:
nt.fetch_component_config = orig_fetch
model_quant.get_dit_args = orig_get_dit
model_quant.get_quant_type = orig_get_qtype
model_quant.do_post_load_quant = orig_do_post
assert MockKwargsTransformer.last_kwargs == {'low_cpu_mem_usage': True}
assert isinstance(transformer, MockKwargsTransformer)
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_end_to_end_with_sibling_partition():
"""Bundled-sibling case: file carries both transformer and sibling weights,
sibling_classes supplies the runtime sibling class, partition routes each
half into its target."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
sibling_dim = 4
raw = {
# Transformer half (after strip).
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
# Sibling half (after strip + sibling partition).
'model.diffusion_model.sibling.in_proj.weight': torch.randn(sibling_dim, sibling_dim),
'model.diffusion_model.sibling.in_proj.bias': torch.zeros(sibling_dim),
'model.diffusion_model.sibling.out_proj.weight': torch.randn(sibling_dim, sibling_dim),
'model.diffusion_model.sibling.out_proj.bias': torch.zeros(sibling_dim),
}
write_fixture(raw, fd, path)
orig_fetch = nt.fetch_component_config
def patched_fetch(_repo, sub):
return {'dim': dim if sub == 'transformer' else sibling_dim}
nt.fetch_component_config = patched_fetch
from modules import model_quant
orig_get_dit = model_quant.get_dit_args
orig_get_qtype = model_quant.get_quant_type
orig_do_post = model_quant.do_post_load_quant
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
model_quant.do_post_load_quant = lambda *a, **k: None
try:
spec = nt.TransformerSpec(
cls=MockMiniTransformer,
siblings={
'sibling': nt.SiblingSpec(
subfolder='sibling',
inline_prefix='sibling.',
acceptable_missing=('rope.',),
),
},
)
transformer, siblings = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
sibling_classes={'sibling': MockMiniTransformer},
)
finally:
nt.fetch_component_config = orig_fetch
model_quant.get_dit_args = orig_get_dit
model_quant.get_quant_type = orig_get_qtype
model_quant.do_post_load_quant = orig_do_post
assert isinstance(transformer, MockMiniTransformer)
assert transformer.in_proj.weight.shape == (dim, dim)
assert 'sibling' in siblings
assert isinstance(siblings['sibling'], MockMiniTransformer)
assert siblings['sibling'].in_proj.weight.shape == (sibling_dim, sibling_dim)
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_raises_on_missing_sibling_class():
"""Sibling keys present in file but caller forgot to supply the class."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
sibling_dim = 4
raw = {
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
'model.diffusion_model.sibling.in_proj.weight': torch.randn(sibling_dim, sibling_dim),
}
write_fixture(raw, fd, path)
orig_fetch = nt.fetch_component_config
nt.fetch_component_config = lambda repo, sub: {'dim': dim}
from modules import model_quant
orig_get_dit = model_quant.get_dit_args
orig_get_qtype = model_quant.get_quant_type
orig_do_post = model_quant.do_post_load_quant
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
model_quant.do_post_load_quant = lambda *a, **k: None
try:
spec = nt.TransformerSpec(
cls=MockMiniTransformer,
siblings={'sibling': nt.SiblingSpec(subfolder='s', inline_prefix='sibling.')},
)
raised = False
try:
nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
sibling_classes={}, # missing!
)
except ValueError as e:
raised = True
assert "'sibling'" in str(e)
assert 'sibling_classes' in str(e)
assert raised, 'expected ValueError'
finally:
nt.fetch_component_config = orig_fetch
model_quant.get_dit_args = orig_get_dit
model_quant.get_quant_type = orig_get_qtype
model_quant.do_post_load_quant = orig_do_post
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_rejects_non_safetensors():
spec = nt.TransformerSpec(cls=MockMiniTransformer)
try:
nt.load(
local_file='/tmp/some.gguf',
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
)
raise AssertionError('expected ValueError')
except ValueError as e:
assert '.safetensors' in str(e)
def crashing_converter(sd):
"""diffusers-style layer count that blows up when the block family is
absent, mirroring convert_chroma_..._to_diffusers on a wrong-arch file."""
return list(set(int(k.split('.')[1]) for k in sd if 'double_blocks.' in k))[-1]
def test_build_component_converter_crash_raises_mismatch():
"""A converter that crashes on wrong-arch keys is wrapped as
OverrideArchMismatch (chaining the original), not the raw IndexError."""
try:
nt.build_component(
component_name='transformer',
state_dict={'blocks.0.self_attn.weight': torch.zeros(2)},
config={'dim': 8},
cls=MockMiniTransformer,
converter=crashing_converter,
acceptable_missing=(),
quant_args={},
quant_type=None,
)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'MockMiniTransformer' in str(e)
assert isinstance(e.__cause__, IndexError), 'original error must be chained'
def test_build_component_shape_mismatch_is_hard_error():
"""A tensor shape mismatch on otherwise-matching keys stays a hard
RuntimeError (the native size-mismatch message), it is NOT converted to
OverrideArchMismatch and so does not silently fall back to base."""
# MockMiniTransformer(dim=8) expects (8, 8) projections; feed (4, 4).
sd = {
'in_proj.weight': torch.randn(4, 4), 'in_proj.bias': torch.zeros(4),
'out_proj.weight': torch.randn(4, 4), 'out_proj.bias': torch.zeros(4),
}
orig_display = nt.errors.display
nt.errors.display = lambda *a, **k: None # silence the expected traceback dump
try:
nt.build_component(
component_name='transformer', state_dict=sd, config={'dim': 8},
cls=MockMiniTransformer, converter=None, acceptable_missing=(),
quant_args={}, quant_type=None,
)
raise AssertionError('expected RuntimeError')
except nt.OverrideArchMismatch:
raise AssertionError('shape mismatch must not be OverrideArchMismatch') from None
except RuntimeError as e:
assert 'size mismatch' in str(e).lower()
finally:
nt.errors.display = orig_display
def test_load_converter_crash_raises_mismatch():
"""End-to-end: a crashing converter surfaces from load() as
OverrideArchMismatch so load_transformer can drop the override."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
raw = {'model.diffusion_model.blocks.0.self_attn.weight': torch.zeros(8, 8)}
write_fixture(raw, fd, path)
orig_fetch = nt.fetch_component_config
nt.fetch_component_config = lambda repo, sub: {'dim': 8}
from modules import model_quant
orig_get_dit = model_quant.get_dit_args
orig_get_qtype = model_quant.get_quant_type
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
try:
spec = nt.TransformerSpec(cls=MockMiniTransformer, converter=crashing_converter)
raised = False
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={})
except nt.OverrideArchMismatch as e:
raised = True
assert 'MockMiniTransformer' in str(e)
assert raised, 'expected OverrideArchMismatch'
finally:
nt.fetch_component_config = orig_fetch
model_quant.get_dit_args = orig_get_dit
model_quant.get_quant_type = orig_get_qtype
finally:
if os.path.exists(path):
os.unlink(path)
# ============================================================
# Ideogram 4 converter (fused community layout -> diffusers layout)
# ============================================================
def ideogram_converter():
from pipelines.ideogram import convert_ideogram4_transformer_checkpoint
return convert_ideogram4_transformer_checkpoint
def test_ideogram4_converter_splits_fused_qkv_weight():
convert = ideogram_converter()
fused = torch.arange(48, dtype=torch.float32).reshape(12, 4)
out = convert({'layers.0.attention.qkv.weight': fused})
assert set(out.keys()) == {f'layers.0.attention.{n}.weight' for n in ('to_q', 'to_k', 'to_v')}
assert torch.equal(out['layers.0.attention.to_q.weight'], fused[0:4])
assert torch.equal(out['layers.0.attention.to_k.weight'], fused[4:8])
assert torch.equal(out['layers.0.attention.to_v.weight'], fused[8:12])
def test_ideogram4_converter_splits_rowwise_scale():
convert = ideogram_converter()
fused = torch.zeros((12, 4), dtype=torch.int8)
scale = torch.arange(12, dtype=torch.float32).reshape(12, 1)
out = convert({'layers.3.attention.qkv.weight': fused, 'layers.3.attention.qkv.weight_scale': scale})
assert torch.equal(out['layers.3.attention.to_q.weight_scale'], scale[0:4])
assert torch.equal(out['layers.3.attention.to_v.weight_scale'], scale[8:12])
assert out['layers.3.attention.to_k.weight_scale'].shape == (4, 1)
def test_ideogram4_converter_scalar_scale_copied_not_sliced():
convert = ideogram_converter()
fused = torch.zeros((12, 4), dtype=torch.int8)
scale = torch.tensor(0.5)
out = convert({'layers.0.attention.qkv.weight': fused, 'layers.0.attention.qkv.weight_scale': scale})
for name in ('to_q', 'to_k', 'to_v'):
assert out[f'layers.0.attention.{name}.weight_scale'] is scale
def test_ideogram4_converter_duplicates_marker():
convert = ideogram_converter()
fused = torch.zeros((12, 4), dtype=torch.int8)
marker = comfy_marker('int8_tensorwise')
out = convert({'layers.0.attention.qkv.weight': fused, 'layers.0.attention.qkv.comfy_quant': marker})
for name in ('to_q', 'to_k', 'to_v'):
assert out[f'layers.0.attention.{name}.comfy_quant'] is marker
def test_ideogram4_converter_renames_o_with_sidecars():
convert = ideogram_converter()
sd = {
'layers.5.attention.o.weight': torch.zeros((4, 4), dtype=torch.int8),
'layers.5.attention.o.weight_scale': torch.tensor(1.0),
'layers.5.attention.o.comfy_quant': comfy_marker('int8_tensorwise'),
}
out = convert(sd)
assert set(out.keys()) == {
'layers.5.attention.to_out.0.weight',
'layers.5.attention.to_out.0.weight_scale',
'layers.5.attention.to_out.0.comfy_quant',
}
def test_ideogram4_converter_passthrough():
convert = ideogram_converter()
sd = {
'input_proj.weight': torch.zeros(4),
'llm_cond_proj.weight': torch.zeros(4),
'layers.0.feed_forward.w1.weight': torch.zeros(4),
'layers.0.attention.norm_q.weight': torch.zeros(4),
'layers.0.adaln_modulation.bias': torch.zeros(4),
'final_layer.linear.weight': torch.zeros(4),
}
out = convert(sd)
assert set(out.keys()) == set(sd.keys())
for k, v in sd.items():
assert out[k] is v
def test_ideogram4_converter_defensive_bias_split():
convert = ideogram_converter()
fused = torch.zeros((12, 4), dtype=torch.float32)
bias = torch.arange(12, dtype=torch.float32)
out = convert({'layers.0.attention.qkv.weight': fused, 'layers.0.attention.qkv.bias': bias})
assert torch.equal(out['layers.0.attention.to_k.bias'], bias[4:8])
def test_ideogram4_converter_does_not_mutate_input():
convert = ideogram_converter()
sd = {
'layers.0.attention.qkv.weight': torch.zeros((12, 4)),
'layers.0.attention.o.weight': torch.zeros((4, 4)),
'input_proj.weight': torch.zeros(4),
}
keys_before = set(sd.keys())
convert(sd)
assert set(sd.keys()) == keys_before
# ============================================================
# Integration: comfy_quant pre-quantized load
# ============================================================
# Same patching strategy as the plain load tests, plus pinned SDNQ opts so the
# builder's settings reads are deterministic. dtype=float32 makes the dequant
# math bit-exact against a manual int8 * scale reference.
def comfy_fixture(dim: int, fmt: str = 'int8_tensorwise') -> dict:
"""comfy_quant export shape: in_proj carries a quantized weight + scalar
fp32 scale + marker; out_proj and biases stay plain f16."""
if fmt == 'float8_e4m3fn':
weight = torch.randn(dim, dim).to(torch.float8_e4m3fn)
else:
weight = torch.randint(-128, 127, (dim, dim), dtype=torch.int8)
return {
'model.diffusion_model.in_proj.weight': weight,
'model.diffusion_model.in_proj.weight_scale': torch.tensor(0.03125, dtype=torch.float32),
'model.diffusion_model.in_proj.comfy_quant': comfy_marker(fmt),
'model.diffusion_model.in_proj.bias': torch.randn(dim, dtype=torch.float16),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim, dtype=torch.float16),
'model.diffusion_model.out_proj.bias': torch.zeros(dim, dtype=torch.float16),
}
class ComfyTestEnv:
"""Patches quant helpers + config fetch and pins the SDNQ opts the
prequantized builder reads, restoring everything on exit.
``fp8_compile_supported`` pins the fp8 storage-remap decision so tests
behave identically regardless of the host GPU and compile settings."""
def __init__(self, dim: int, fp8_compile_supported: bool = True):
self.dim = dim
self.fp8_compile_supported = fp8_compile_supported
def __enter__(self):
from modules import model_quant, shared
from sdnq import common as sdnq_common
from sdnq import kernel_wrappers
self.shared = shared
self.sdnq_common = sdnq_common
self.kernel_wrappers = kernel_wrappers
self.orig_fetch = nt.fetch_component_config
self.orig_get_dit = model_quant.get_dit_args
self.orig_get_qtype = model_quant.get_quant_type
self.orig_do_post = model_quant.do_post_load_quant
self.orig_fp8_supported = kernel_wrappers.is_fp8_compile_supported
self.orig_check_compile = sdnq_common.check_torch_compile
self.model_quant = model_quant
nt.fetch_component_config = lambda repo, sub: {'dim': self.dim}
model_quant.get_dit_args = lambda *a, **k: ({}, {})
model_quant.get_quant_type = lambda *a, **k: None
model_quant.do_post_load_quant = lambda *a, **k: None
kernel_wrappers.is_fp8_compile_supported = self.fp8_compile_supported
sdnq_common.check_torch_compile = lambda: not self.fp8_compile_supported
self.orig_opts = {
'sdnq_quantize_matmul_mode': shared.opts.sdnq_quantize_matmul_mode,
'sdnq_dequantize_fp32': shared.opts.sdnq_dequantize_fp32,
'diffusers_offload_mode': shared.opts.diffusers_offload_mode,
}
shared.opts.data['sdnq_quantize_matmul_mode'] = 'disabled'
shared.opts.data['sdnq_dequantize_fp32'] = True
shared.opts.data['diffusers_offload_mode'] = 'model' # force CPU placement
return self
def __exit__(self, *exc):
nt.fetch_component_config = self.orig_fetch
self.model_quant.get_dit_args = self.orig_get_dit
self.model_quant.get_quant_type = self.orig_get_qtype
self.model_quant.do_post_load_quant = self.orig_do_post
self.kernel_wrappers.is_fp8_compile_supported = self.orig_fp8_supported
self.sdnq_common.check_torch_compile = self.orig_check_compile
for key, value in self.orig_opts.items():
self.shared.opts.data[key] = value
return False
def test_load_comfy_int8_end_to_end():
"""Full pipeline on a comfy_quant fixture: the marked linear becomes an
SDNQ int8 layer holding the file's exact tensors, the unmarked linear
loads plain, and the forward pass matches a dequantized reference."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, siblings = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
assert siblings == {}
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear', f'marked layer is {in_proj.__class__.__name__}'
dq = in_proj.sdnq_dequantizer
assert dq.weights_dtype == 'int8'
assert dq.group_size == -1
assert dq.use_quantized_matmul is False
assert dq.original_shape == (dim, dim)
# File tensors adopted bit-exact: int8 codes and fp32 scalar scale.
assert in_proj.weight.dtype == torch.int8
assert torch.equal(in_proj.weight.detach().cpu(), raw['model.diffusion_model.in_proj.weight'])
assert in_proj.scale.dtype == torch.float32
assert tuple(in_proj.scale.shape) == (1, 1)
assert in_proj.scale.item() == raw['model.diffusion_model.in_proj.weight_scale'].item()
assert in_proj.zero_point is None
# Dequant matches comfy semantics exactly: fp = int8.float() * scale.
expected = raw['model.diffusion_model.in_proj.weight'].float() * raw['model.diffusion_model.in_proj.weight_scale']
dequantized = dq(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), expected)
# Forward parity against a reference built from the dequantized weight.
x = torch.randn(2, dim)
out = in_proj(x)
ref = torch.nn.functional.linear(x, expected, in_proj.bias.detach().cpu())
assert torch.allclose(out.detach().cpu(), ref, atol=1e-6)
# Unmarked layer loads as a plain Linear cast to the target dtype.
assert transformer.out_proj.__class__ is torch.nn.Linear
assert transformer.out_proj.weight.dtype == torch.float32
assert torch.allclose(
transformer.out_proj.weight.detach().cpu(),
raw['model.diffusion_model.out_proj.weight'].float(),
)
# Marked as SDNQ-quantized so downstream never re-quantizes.
assert getattr(transformer, 'quantization_config', None) is not None
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_fp8_end_to_end():
"""float8_e4m3fn variant: same container, fp8 storage. The marked linear
must keep fp8 codes and dequantize as weight.float() * scale."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim, fmt='float8_e4m3fn')
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear'
assert in_proj.sdnq_dequantizer.weights_dtype == 'float8_e4m3fn'
assert in_proj.weight.dtype == torch.float8_e4m3fn
assert in_proj.scale.dtype == torch.float32
expected = raw['model.diffusion_model.in_proj.weight'].float() * raw['model.diffusion_model.in_proj.weight_scale']
dequantized = in_proj.sdnq_dequantizer(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), expected)
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_fp8_remaps_storage_when_compile_unsupported():
"""When compiled dequant is on but the hardware cannot compile e4m3,
fp8 weights are adopted through the uint8-backed codec: identical
decoded values from storage the compiler can always touch."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim, fmt='float8_e4m3fn')
write_fixture(raw, fd, path)
with ComfyTestEnv(dim, fp8_compile_supported=False):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear'
assert in_proj.sdnq_dequantizer.weights_dtype == 'float8_e4m3fn_sdnq'
assert in_proj.weight.dtype == torch.uint8
expected = raw['model.diffusion_model.in_proj.weight'].float() * raw['model.diffusion_model.in_proj.weight_scale']
dequantized = in_proj.sdnq_dequantizer(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), expected)
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_metadata_end_to_end():
"""Marker-less container: quant info only in the header
_quantization_metadata, layer names carrying the file's tensor prefix.
The load path must re-key through the prefix strip and land on the same
prequantized build as the marker form."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
del raw['model.diffusion_model.in_proj.comfy_quant']
layers = {'model.diffusion_model.in_proj': {'format': 'int8_tensorwise'}}
write_fixture(raw, fd, path, metadata=quant_metadata(layers))
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear'
assert in_proj.weight.dtype == torch.int8
assert torch.equal(in_proj.weight.detach().cpu(), raw['model.diffusion_model.in_proj.weight'])
assert transformer.out_proj.__class__ is torch.nn.Linear
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_metadata_bare_names_end_to_end():
"""Marker-less container without a tensor prefix (official Comfy-Org
exports): metadata names match the bare keys and need no re-keying."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = {key[len('model.diffusion_model.'):]: value for key, value in comfy_fixture(dim).items()}
del raw['in_proj.comfy_quant']
raw['in_proj.input_scale'] = torch.tensor(0.125, dtype=torch.float32)
layers = {'in_proj': {'format': 'int8_tensorwise'}}
write_fixture(raw, fd, path, metadata=quant_metadata(layers))
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
assert transformer.in_proj.__class__.__name__ == 'SDNQLinear'
assert torch.equal(transformer.in_proj.weight.detach().cpu(), raw['in_proj.weight'])
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_full_precision_mm_excluded_from_matmul():
"""Layers flagged full_precision_matrix_mult land on the config's
per-layer matmul exclusion list, which apply_sdnq_options_to_model
honors when the user enables quantized matmul."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
import json
raw = comfy_fixture(dim)
payload = {'format': 'int8_tensorwise', 'full_precision_matrix_mult': True}
raw['model.diffusion_model.in_proj.comfy_quant'] = torch.tensor(list(json.dumps(payload).encode()), dtype=torch.uint8)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
assert 'in_proj.weight' in transformer.quantization_config.modules_to_not_use_matmul
assert transformer.in_proj.sdnq_dequantizer.use_quantized_matmul is False
finally:
if os.path.exists(path):
os.unlink(path)
def nvfp4_fixture(dim: int, with_marker: bool = True) -> tuple[dict, torch.Tensor]:
"""A valid nvfp4 container for one marked linear, plus the reference
dequantized weight per the format definition: E2M1 decode * unswizzled
e4m3 block scale * fp32 global scale."""
import json
groups = dim // 16
gen = torch.Generator().manual_seed(7)
codes = torch.randint(0, 16, (dim, dim), generator=gen, dtype=torch.uint8)
block_scales = (torch.rand(dim, groups, generator=gen) * 2 + 0.5).to(torch.float8_e4m3fn)
global_scale = torch.tensor(0.0125, dtype=torch.float32)
lut = torch.tensor(E2M1_VALUES, dtype=torch.float32)
reference = lut[codes.long()].reshape(dim, groups, 16) * (block_scales.float() * global_scale).unsqueeze(-1)
reference = reference.reshape(dim, dim)
packed = torch.bitwise_or(torch.bitwise_left_shift(codes[:, 0::2], 4), codes[:, 1::2]) # even element in the high nibble
sd = {
'model.diffusion_model.in_proj.weight': packed,
'model.diffusion_model.in_proj.weight_scale': to_blocked_reference(block_scales),
'model.diffusion_model.in_proj.weight_scale_2': global_scale,
'model.diffusion_model.in_proj.bias': torch.randn(dim, generator=gen).to(torch.float16),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim, generator=gen).to(torch.float16),
'model.diffusion_model.out_proj.bias': torch.zeros(dim, dtype=torch.float16),
}
if with_marker:
marker = json.dumps({'format': 'nvfp4', 'group_size': 16, 'orig_dtype': 'torch.float16', 'orig_shape': [dim, dim]})
sd['model.diffusion_model.in_proj.comfy_quant'] = torch.tensor(list(marker.encode()), dtype=torch.uint8)
return sd, reference
def test_load_comfy_nvfp4_end_to_end():
"""Full pipeline on an nvfp4 container: packed uint8 weights survive with
swapped nibble order, block scales unswizzle and fold with the global
scale into fp32 grouped scales, and the dequantized weight matches the
format reference exactly."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 32
raw, reference = nvfp4_fixture(dim)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear'
dq = in_proj.sdnq_dequantizer
assert dq.weights_dtype == 'float4_e2m1fn'
assert dq.group_size == 16
assert tuple(dq.quantized_weight_shape) == (dim, dim // 16, 16)
assert dq.re_quantize_for_matmul is True
assert in_proj.weight.dtype == torch.uint8
assert tuple(in_proj.weight.shape) == (dim, dim // 2)
assert in_proj.scale.dtype == torch.float32
assert tuple(in_proj.scale.shape) == (dim, dim // 16, 1)
dequantized = dq(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), reference)
x = torch.randn(2, dim)
out = in_proj(x)
ref = torch.nn.functional.linear(x, reference, in_proj.bias.detach().cpu())
assert torch.allclose(out.detach().cpu(), ref, atol=1e-5)
assert transformer.out_proj.__class__ is torch.nn.Linear
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_nvfp4_metadata_form():
"""Marker-less nvfp4 (header metadata only, no orig_shape/group_size):
the format constant and module dimensions fill the gaps."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 32
raw, reference = nvfp4_fixture(dim, with_marker=False)
layers = {'model.diffusion_model.in_proj': {'format': 'nvfp4'}}
write_fixture(raw, fd, path, metadata=quant_metadata(layers))
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(
local_file=path,
repo_id='fake/repo',
spec=spec,
diffusers_cfg={},
dtype=torch.float32,
)
in_proj = transformer.in_proj
assert in_proj.__class__.__name__ == 'SDNQLinear'
assert in_proj.sdnq_dequantizer.weights_dtype == 'float4_e2m1fn'
dequantized = in_proj.sdnq_dequantizer(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), reference)
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_nvfp4_orig_shape_mismatch_raises():
"""A marker whose orig_shape disagrees with the model config is a wrong
file for the class; reject so the base-repo fallback engages."""
import json
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 32
raw, _ = nvfp4_fixture(dim)
marker = json.dumps({'format': 'nvfp4', 'group_size': 16, 'orig_shape': [dim, dim * 2]})
raw['model.diffusion_model.in_proj.comfy_quant'] = torch.tensor(list(marker.encode()), dtype=torch.uint8)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={}, dtype=torch.float32)
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'orig_shape' in str(e)
finally:
if os.path.exists(path):
os.unlink(path)
def test_detect_comfy_mixed_formats_raises():
"""One file mixing int8 and fp8 layers has no single SDNQ mapping; reject."""
sd = {
'a.weight': torch.zeros((4, 4), dtype=torch.int8),
'a.comfy_quant': comfy_marker('int8_tensorwise'),
'b.weight': torch.zeros((4, 4), dtype=torch.float8_e4m3fn),
'b.comfy_quant': comfy_marker('float8_e4m3fn'),
}
try:
nt.detect_comfy_quant(sd, 'Test')
raise AssertionError('expected OverrideArchMismatch')
except nt.OverrideArchMismatch as e:
assert 'mixes formats' in str(e)
def test_load_comfy_marker_dtype_mismatch_raises():
"""A marker whose declared format does not match the stored weight dtype
(mislabeled container) must be rejected, not silently misinterpreted."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
raw['model.diffusion_model.in_proj.weight'] = torch.randn(dim, dim, dtype=torch.float16) # marker says int8
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
raised = False
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={})
except nt.OverrideArchMismatch as e:
raised = True
assert 'torch.int8' in str(e) and 'torch.float16' in str(e)
assert raised, 'expected OverrideArchMismatch'
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_unsupported_format_raises_mismatch():
"""A comfy_quant file in a format sdnext cannot adopt must surface as
OverrideArchMismatch so load_transformer falls back to the base repo."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
raw['model.diffusion_model.in_proj.comfy_quant'] = comfy_marker('float8_e4m3fn_scaled')
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
raised = False
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={})
except nt.OverrideArchMismatch as e:
raised = True
assert 'float8_e4m3fn_scaled' in str(e)
assert raised, 'expected OverrideArchMismatch'
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_marker_for_unknown_module_raises_mismatch():
"""A marker naming a module the target class does not have means the file
belongs to a different arch; must fall back, not crash."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
raw['model.diffusion_model.ghost.weight'] = torch.zeros((dim, dim), dtype=torch.int8)
raw['model.diffusion_model.ghost.weight_scale'] = torch.tensor(1.0)
raw['model.diffusion_model.ghost.comfy_quant'] = comfy_marker('int8_tensorwise')
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
raised = False
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={})
except nt.OverrideArchMismatch as e:
raised = True
assert 'ghost' in str(e)
assert raised, 'expected OverrideArchMismatch'
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_transformer_syncs_loaded_unet():
"""A full model load that consumes the UNET dropdown override must mark it
as loaded, so the queued sd_unet onchange callback does not trigger a
second, redundant full model reload."""
from modules import sd_unet, shared
from pipelines import generic_transformer as gt
fd, path = tempfile.mkstemp(suffix='.safetensors')
dim = 8
raw = {
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
}
write_fixture(raw, fd, path)
orig_unet_opt = shared.opts.sd_unet
orig_loaded = sd_unet.loaded_unet
sd_unet.unet_dict['mock-unet'] = path
shared.opts.data['sd_unet'] = 'mock-unet'
sd_unet.loaded_unet = None
try:
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer = gt.load_transformer('fake/repo', cls_name=MockMiniTransformer, native_spec=spec)
assert transformer is not None
assert sd_unet.loaded_unet == 'mock-unet', f'override consumed but loaded_unet={sd_unet.loaded_unet}'
finally:
sd_unet.unet_dict.pop('mock-unet', None)
shared.opts.data['sd_unet'] = orig_unet_opt
sd_unet.loaded_unet = orig_loaded
if os.path.exists(path):
os.unlink(path)
class MockFusedAttention(torch.nn.Module):
"""Split-attention module tree matching Ideogram4Transformer2DModel's
naming (to_q/to_k/to_v + to_out ModuleList), fed by fused checkpoints."""
def __init__(self, dim: int):
super().__init__()
self.to_q = torch.nn.Linear(dim, dim, bias=False)
self.to_k = torch.nn.Linear(dim, dim, bias=False)
self.to_v = torch.nn.Linear(dim, dim, bias=False)
self.to_out = torch.nn.ModuleList([torch.nn.Linear(dim, dim, bias=False), torch.nn.Dropout(0.0)])
class MockFusedBlock(torch.nn.Module):
def __init__(self, dim: int):
super().__init__()
self.attention = MockFusedAttention(dim)
class MockFusedTransformer(torch.nn.Module):
@classmethod
def from_config(cls, config: dict, **kwargs) -> 'MockFusedTransformer':
return cls(dim=config['dim'])
def __init__(self, dim: int):
super().__init__()
self.layers = torch.nn.ModuleList([MockFusedBlock(dim)])
self.input_proj = torch.nn.Linear(dim, dim)
def fused_spec():
from pipelines.ideogram import convert_ideogram4_transformer_checkpoint
return nt.TransformerSpec(
cls=MockFusedTransformer,
converter=convert_ideogram4_transformer_checkpoint,
converter_handles_quant=True,
)
def fused_fixture(dim: int, quantized: bool = True) -> dict:
if quantized:
qkv = torch.randint(-128, 127, (3 * dim, dim), dtype=torch.int8)
else:
qkv = torch.randn(3 * dim, dim, dtype=torch.float16)
raw = {
'model.diffusion_model.layers.0.attention.qkv.weight': qkv,
'model.diffusion_model.layers.0.attention.o.weight': torch.randn(dim, dim, dtype=torch.float16),
'model.diffusion_model.input_proj.weight': torch.randn(dim, dim, dtype=torch.float16),
'model.diffusion_model.input_proj.bias': torch.zeros(dim, dtype=torch.float16),
}
if quantized:
raw['model.diffusion_model.layers.0.attention.qkv.weight_scale'] = torch.rand(3 * dim, 1, dtype=torch.float32)
raw['model.diffusion_model.layers.0.attention.qkv.comfy_quant'] = comfy_marker('int8_tensorwise')
return raw
def test_load_fused_comfy_end_to_end():
"""Quant-aware converter + comfy adoption: fused int8 qkv with row-wise
scales lands as three SDNQ linears holding the exact row slices."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = fused_fixture(dim, quantized=True)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
transformer, _ = nt.load(local_file=path, repo_id='fake/repo', spec=fused_spec(), diffusers_cfg={}, dtype=torch.float32)
attn = transformer.layers[0].attention
fused_w = raw['model.diffusion_model.layers.0.attention.qkv.weight']
fused_s = raw['model.diffusion_model.layers.0.attention.qkv.weight_scale']
for i, name in enumerate(('to_q', 'to_k', 'to_v')):
layer = getattr(attn, name)
assert layer.__class__.__name__ == 'SDNQLinear', f'{name} is {layer.__class__.__name__}'
assert layer.weight.dtype == torch.int8
assert torch.equal(layer.weight.detach().cpu(), fused_w[i * dim:(i + 1) * dim])
assert tuple(layer.scale.shape) == (dim, 1), f'{name} scale shape {layer.scale.shape}'
assert torch.equal(layer.scale.detach().cpu(), fused_s[i * dim:(i + 1) * dim])
expected = fused_w[i * dim:(i + 1) * dim].float() * fused_s[i * dim:(i + 1) * dim]
dequantized = layer.sdnq_dequantizer(layer.weight, layer.scale, zero_point=None, svd_up=None, svd_down=None)
assert torch.equal(dequantized.detach().cpu(), expected)
# unmarked o.weight passes through the rename as a plain Linear
assert attn.to_out[0].__class__ is torch.nn.Linear
assert torch.allclose(attn.to_out[0].weight.detach().cpu(), raw['model.diffusion_model.layers.0.attention.o.weight'].float())
assert transformer.input_proj.__class__ is torch.nn.Linear
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_fused_bf16_end_to_end():
"""Converter-first on a plain float fused file: no markers, standard load
path, split weights land on the class-native linears."""
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = fused_fixture(dim, quantized=False)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
transformer, _ = nt.load(local_file=path, repo_id='fake/repo', spec=fused_spec(), diffusers_cfg={}, dtype=torch.float32)
attn = transformer.layers[0].attention
fused_w = raw['model.diffusion_model.layers.0.attention.qkv.weight']
for i, name in enumerate(('to_q', 'to_k', 'to_v')):
layer = getattr(attn, name)
assert layer.__class__ is torch.nn.Linear
assert torch.allclose(layer.weight.detach().cpu(), fused_w[i * dim:(i + 1) * dim].float())
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_fused_convrot_end_to_end():
"""Fused qkv with a convrot marker: the converter duplicates the marker to
the three split layers, each of which loads with hadamard geometry. Row
slicing commutes with the in-features rotation, so slices stay lossless."""
import json as json_mod
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim, group_size = 8, 4
raw = fused_fixture(dim, quantized=True)
payload = json_mod.dumps({'format': 'int8_tensorwise', 'convrot': True, 'convrot_groupsize': group_size, 'per_row': True})
raw['model.diffusion_model.layers.0.attention.qkv.comfy_quant'] = torch.tensor(list(payload.encode()), dtype=torch.uint8)
raw['model.diffusion_model.layers.0.attention.qkv.weight_scale'] = torch.rand(3 * dim, 1, dtype=torch.float32)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
transformer, _ = nt.load(local_file=path, repo_id='fake/repo', spec=fused_spec(), diffusers_cfg={}, dtype=torch.float32)
attn = transformer.layers[0].attention
for name in ('to_q', 'to_k', 'to_v'):
dq = getattr(attn, name).sdnq_dequantizer
assert dq.use_hadamard is True, f'{name} lost the convrot flag'
assert dq.hadamard_group_size == group_size
finally:
if os.path.exists(path):
os.unlink(path)
def test_build_component_comfy_skips_converter_when_not_quant_aware():
"""With converter_handles_quant=False (the default for every other arch),
a comfy state dict must reach the prequantized path without the converter
ever running; a crashing converter proves it was not invoked."""
dim = 8
raw = comfy_fixture(dim)
sd = {k[len('model.diffusion_model.'):]: v for k, v in raw.items()}
with ComfyTestEnv(dim):
component = nt.build_component(
component_name='transformer',
state_dict=sd,
config={'dim': dim},
cls=MockMiniTransformer,
converter=crashing_converter,
acceptable_missing=('rope.',),
quant_args={},
quant_type='SDNQConfig',
dtype=torch.float32,
)
assert component.in_proj.__class__.__name__ == 'SDNQLinear'
def test_load_transformer_secondary_slot_syncs_tracker():
"""The secondary slot consumes sd_unet_secondary and syncs its own
tracker without touching the primary slot."""
from modules import sd_unet, shared
from pipelines import generic_transformer as gt
fd, path = tempfile.mkstemp(suffix='.safetensors')
dim = 8
raw = {
'model.diffusion_model.in_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.in_proj.bias': torch.zeros(dim),
'model.diffusion_model.out_proj.weight': torch.randn(dim, dim),
'model.diffusion_model.out_proj.bias': torch.zeros(dim),
}
write_fixture(raw, fd, path)
orig_primary_opt = shared.opts.sd_unet
orig_secondary_opt = shared.opts.sd_unet_secondary
orig_primary = sd_unet.loaded_unet
orig_secondary = sd_unet.loaded_unet_secondary
sd_unet.unet_dict['mock-unet-2'] = path
shared.opts.data['sd_unet'] = 'Default'
shared.opts.data['sd_unet_secondary'] = 'mock-unet-2'
sd_unet.loaded_unet = None
sd_unet.loaded_unet_secondary = None
try:
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer = gt.load_transformer('fake/repo', cls_name=MockMiniTransformer, native_spec=spec, override_slot='secondary')
assert transformer is not None
assert sd_unet.loaded_unet_secondary == 'mock-unet-2'
assert sd_unet.loaded_unet is None, 'primary tracker must stay untouched'
finally:
sd_unet.unet_dict.pop('mock-unet-2', None)
shared.opts.data['sd_unet'] = orig_primary_opt
shared.opts.data['sd_unet_secondary'] = orig_secondary_opt
sd_unet.loaded_unet = orig_primary
sd_unet.loaded_unet_secondary = orig_secondary
if os.path.exists(path):
os.unlink(path)
def regular_hadamard(n: int) -> torch.Tensor:
"""Regular Hadamard construction the ConvRot format and SDNQ share:
4x4 base, Kronecker recursion, 1/sqrt(n) normalization. Symmetric and
involutory, so rotation and inverse are the same matrix."""
h4 = torch.tensor([[1., 1., 1., -1.], [1., 1., -1., 1.], [1., -1., 1., 1.], [-1., 1., 1., 1.]])
h = h4
size = 4
while size < n:
h = torch.kron(h, h4)
size *= 4
return h / (n ** 0.5)
def comfy_convrot_quantize(weight: torch.Tensor, group_size: int):
"""Reference ConvRot int8 quantizer (mirrors the ComfyUI runtime): rotate
grouped in-features by the regular Hadamard, then row-wise symmetric int8."""
h = regular_hadamard(group_size)
out_f, in_f = weight.shape
rotated = (weight.reshape(out_f, -1, group_size) @ h.T).reshape(out_f, in_f)
scale = rotated.abs().amax(dim=-1, keepdim=True) / 127
q = rotated.div(scale).round().clamp(-128, 127).to(torch.int8)
return q, scale
def test_load_comfy_convrot_end_to_end():
"""ConvRot parity: a layer quantized with the reference ConvRot math
loads as an SDNQ hadamard layer whose dequant matches the reference dequant
and recovers the original weight within int8 quantization error. The plain
second layer proves per-layer mixing within one file."""
import json as json_mod
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim, group_size = 8, 4
original = torch.randn(dim, dim)
q, scale = comfy_convrot_quantize(original, group_size)
marker = json_mod.dumps({'format': 'int8_tensorwise', 'convrot': True, 'convrot_groupsize': group_size, 'per_row': True})
raw = {
'model.diffusion_model.in_proj.weight': q,
'model.diffusion_model.in_proj.weight_scale': scale,
'model.diffusion_model.in_proj.comfy_quant': torch.tensor(list(marker.encode()), dtype=torch.uint8),
'model.diffusion_model.in_proj.bias': torch.zeros(dim, dtype=torch.float16),
'model.diffusion_model.out_proj.weight': torch.randint(-128, 127, (dim, dim), dtype=torch.int8),
'model.diffusion_model.out_proj.weight_scale': torch.tensor(0.03125, dtype=torch.float32),
'model.diffusion_model.out_proj.comfy_quant': comfy_marker('int8_tensorwise'),
'model.diffusion_model.out_proj.bias': torch.zeros(dim, dtype=torch.float16),
}
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
transformer, _ = nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={}, dtype=torch.float32)
in_proj = transformer.in_proj
dq = in_proj.sdnq_dequantizer
assert dq.use_hadamard is True
assert dq.hadamard_group_size == group_size
assert tuple(in_proj.scale.shape) == (dim, 1)
dequantized = dq(in_proj.weight, in_proj.scale, zero_point=None, svd_up=None, svd_down=None).detach().cpu()
h = regular_hadamard(group_size)
reference = ((q.float() * scale).reshape(dim, -1, group_size) @ h.T).reshape(dim, dim)
assert torch.allclose(dequantized, reference, atol=1e-5), 'SDNQ hadamard dequant diverges from the ConvRot reference'
assert torch.allclose(dequantized, original, atol=0.15), 'dequant does not recover the original weight'
# plain layer in the same file stays rotation-free
assert transformer.out_proj.sdnq_dequantizer.use_hadamard is False
finally:
if os.path.exists(path):
os.unlink(path)
def test_load_comfy_convrot_nondivisible_falls_back():
"""A convrot group size that does not divide in_features cannot be undone;
the file must fall back to the base repo."""
import json as json_mod
fd, path = tempfile.mkstemp(suffix='.safetensors')
try:
dim = 8
raw = comfy_fixture(dim)
marker = json_mod.dumps({'format': 'int8_tensorwise', 'convrot': True, 'convrot_groupsize': 16})
raw['model.diffusion_model.in_proj.comfy_quant'] = torch.tensor(list(marker.encode()), dtype=torch.uint8)
write_fixture(raw, fd, path)
with ComfyTestEnv(dim):
spec = nt.TransformerSpec(cls=MockMiniTransformer)
raised = False
try:
nt.load(local_file=path, repo_id='fake/repo', spec=spec, diffusers_cfg={})
except nt.OverrideArchMismatch as e:
raised = True
assert 'does not divide' in str(e)
assert raised, 'expected OverrideArchMismatch'
finally:
if os.path.exists(path):
os.unlink(path)
def test_build_component_comfy_preempts_sdnq_fresh_quant():
"""When SDNQ on-load quant settings are active (quant_type=SDNQConfig), a
comfy_quant file must still take the pre-quantized path: fresh quant of
int8 data would corrupt it. quant_args={} proves the comfy branch never
reaches build_component_quantized (which requires a quantization_config)."""
dim = 8
raw = comfy_fixture(dim)
sd = {k[len('model.diffusion_model.'):]: v for k, v in raw.items()}
with ComfyTestEnv(dim):
component = nt.build_component(
component_name='transformer',
state_dict=sd,
config={'dim': dim},
cls=MockMiniTransformer,
converter=None,
acceptable_missing=('rope.',),
quant_args={},
quant_type='SDNQConfig',
dtype=torch.float32,
)
assert component.in_proj.__class__.__name__ == 'SDNQLinear'
assert component.in_proj.weight.dtype == torch.int8
# ============================================================
# Run
# ============================================================
def run_all():
log.warning('=== drop_companion_keys ===')
cat = category('companion')
for fn in [
test_drop_companion_keys_no_companions_pass_through,
test_drop_companion_keys_filters_all_in_one_layout,
test_drop_companion_keys_raises_when_nothing_left,
test_drop_companion_keys_empty_prefix_tuple_no_op,
test_drop_then_strip_all_in_one_layout,
]:
run_test(cat, fn)
log.warning('=== strip_prefix ===')
cat = category('strip')
for fn in [
test_strip_prefix_bare_keys_pass_through,
test_strip_prefix_dominant_single_variant,
test_strip_prefix_picks_longest_match_first,
test_strip_prefix_mixed_prefixes_raises,
test_strip_prefix_net_variant,
test_strip_prefix_diffusion_model_variant,
test_strip_prefix_custom_prefix_set,
]:
run_test(cat, fn)
log.warning('=== partition_siblings ===')
cat = category('partition')
for fn in [
test_partition_siblings_empty_spec_returns_state_dict_unchanged,
test_partition_siblings_no_matches_keeps_all_in_transformer,
test_partition_siblings_single_sibling_split,
test_partition_siblings_multiple_siblings,
]:
run_test(cat, fn)
log.warning('=== forbidden_markers ===')
cat = category('forbidden')
for fn in [
test_forbidden_markers_passes_when_absent,
test_forbidden_markers_raises_when_present,
test_forbidden_markers_empty_tuple_no_op,
]:
run_test(cat, fn)
log.warning('=== comfy_quant detection / remap ===')
cat = category('comfy')
for fn in [
test_detect_comfy_no_markers_returns_none,
test_detect_comfy_valid_markers,
test_detect_comfy_unsupported_format_raises,
test_detect_comfy_malformed_marker_raises,
test_detect_comfy_convrot_accepted,
test_detect_comfy_convrot_bad_groupsize_raises,
test_detect_comfy_marker_missing_format_field_raises,
test_remap_comfy_renames_and_reshapes_scale,
test_remap_comfy_passes_unmarked_keys_verbatim,
test_remap_comfy_does_not_mutate_input,
test_remap_comfy_drops_input_scale,
test_read_quant_metadata_absent_returns_none,
test_read_quant_metadata_parses_layers,
test_read_quant_metadata_malformed_raises,
test_read_quant_metadata_non_dict_layers_raises,
test_transcode_quant_metadata_synthesizes_markers,
test_transcode_quant_metadata_header_wins,
test_transcode_quant_metadata_no_matches_is_noop,
]:
run_test(cat, fn)
log.warning('=== nvfp4 codec / unswizzle / load ===')
cat = category('nvfp4')
for fn in [
test_unswizzle_block_scales_roundtrip,
test_nvfp4_codec_ocp_table,
test_nvfp4_pack_ocp_grid_roundtrip,
test_detect_comfy_nvfp4_marker_accepted,
test_detect_comfy_nvfp4_bad_group_size_raises,
test_detect_comfy_nvfp4_convrot_raises,
test_load_comfy_nvfp4_end_to_end,
test_load_comfy_nvfp4_metadata_form,
test_load_comfy_nvfp4_orig_shape_mismatch_raises,
]:
run_test(cat, fn)
log.warning('=== model_probe quant detection ===')
cat = category('probe')
for fn in [
test_probe_detects_header_quant_metadata,
test_probe_resolves_nvfp4_from_weight_dtype,
test_probe_fp8_markers_despite_u8_noise,
test_probe_int8_markers,
test_probe_plain_file_no_quant,
]:
run_test(cat, fn)
log.warning('=== noop_converter detection ===')
cat = category('noop')
for fn in [
test_noop_converter_identity_lambda,
test_noop_converter_real_function,
test_noop_converter_lambda_with_modification,
]:
run_test(cat, fn)
log.warning('=== validate_state_dict_load ===')
cat = category('validate')
for fn in [
test_validate_accepts_buffer_only_missing,
test_validate_rejects_unexpected,
test_validate_rejects_hard_missing,
test_validate_empty_passes,
]:
run_test(cat, fn)
log.warning('=== make_default_spec ===')
cat = category('default_spec')
for fn in [
test_make_default_spec_for_unknown_class,
test_make_default_spec_picks_up_real_diffusers_converter,
test_make_default_spec_skips_qwen_image_noop,
]:
run_test(cat, fn)
log.warning('=== auto_pickup_converter ===')
cat = category('autopickup')
for fn in [
test_auto_pickup_returns_none_for_unknown_class,
test_auto_pickup_returns_real_diffusers_converter,
test_auto_pickup_skips_noop_converter_qwen,
]:
run_test(cat, fn)
log.warning('=== TransformerSpec / SiblingSpec ===')
cat = category('specs')
for fn in [
test_transformer_spec_defaults,
test_sibling_spec_defaults,
test_transformer_spec_is_frozen,
]:
run_test(cat, fn)
log.warning('=== end-to-end load ===')
cat = category('load')
for fn in [
test_load_end_to_end_with_bfl_prefix_no_converter,
test_load_forwards_kwargs_to_from_config,
test_load_end_to_end_with_sibling_partition,
test_load_raises_on_missing_sibling_class,
test_load_rejects_non_safetensors,
test_build_component_converter_crash_raises_mismatch,
test_build_component_shape_mismatch_is_hard_error,
test_load_converter_crash_raises_mismatch,
]:
run_test(cat, fn)
log.warning('=== comfy_quant load ===')
cat = category('comfy_load')
for fn in [
test_load_comfy_int8_end_to_end,
test_load_comfy_fp8_end_to_end,
test_load_comfy_fp8_remaps_storage_when_compile_unsupported,
test_load_comfy_metadata_end_to_end,
test_load_comfy_metadata_bare_names_end_to_end,
test_load_comfy_full_precision_mm_excluded_from_matmul,
test_detect_comfy_mixed_formats_raises,
test_load_comfy_marker_dtype_mismatch_raises,
test_load_comfy_unsupported_format_raises_mismatch,
test_load_comfy_marker_for_unknown_module_raises_mismatch,
test_load_comfy_convrot_end_to_end,
test_load_comfy_convrot_nondivisible_falls_back,
test_load_transformer_syncs_loaded_unet,
test_load_transformer_secondary_slot_syncs_tracker,
test_build_component_comfy_preempts_sdnq_fresh_quant,
test_build_component_comfy_skips_converter_when_not_quant_aware,
]:
run_test(cat, fn)
log.warning('=== ideogram4 converter / fused load ===')
cat = category('ideogram4')
for fn in [
test_ideogram4_converter_splits_fused_qkv_weight,
test_ideogram4_converter_splits_rowwise_scale,
test_ideogram4_converter_scalar_scale_copied_not_sliced,
test_ideogram4_converter_duplicates_marker,
test_ideogram4_converter_renames_o_with_sidecars,
test_ideogram4_converter_passthrough,
test_ideogram4_converter_defensive_bias_split,
test_ideogram4_converter_does_not_mutate_input,
test_load_fused_comfy_end_to_end,
test_load_fused_bf16_end_to_end,
test_load_fused_convrot_end_to_end,
]:
run_test(cat, fn)
log.warning('=== Results ===')
total_passed = 0
total_failed = 0
for cat_name, info in results.items():
ok = info['failed'] == 0
status = 'PASS' if ok else 'FAIL'
log.info(f" {cat_name}: {info['passed']} passed, {info['failed']} failed [{status}]")
total_passed += info['passed']
total_failed += info['failed']
log.warning(f'Total: {total_passed} passed, {total_failed} failed')
return total_failed == 0
if __name__ == '__main__':
import time
t0 = time.time()
ok = run_all()
log.warning(f'Total time: {time.time() - t0:.2f}s')
sys.exit(0 if ok else 1)