Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions convert_to_quant/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,6 @@ def extract_filter_flags(args) -> dict:
if getattr(args, name):
flags[name] = True

# Text model aliases: these flags imply generic_text input_scale behavior
TEXT_MODEL_ALIASES = {"qwen35"}
if any(flags.get(alias) for alias in TEXT_MODEL_ALIASES):
flags["generic_text"] = True

return flags


Expand Down Expand Up @@ -325,7 +320,7 @@ def run_conversion(args):
# Handle NVFP4 quantization mode (separate workflow OR unified if mixing formats)
if args.nvfp4:
# Check if we need mixed format support
needs_mixing = args.custom_type or args.fallback
needs_mixing = args.custom_type or args.fallback or args.layer_config

if needs_mixing:
# Route through unified path for mixed format support
Expand Down Expand Up @@ -447,7 +442,7 @@ def run_conversion(args):
# Handle MXFP8 quantization mode (separate workflow OR unified if mixing formats)
if args.mxfp8:
# Check if we need mixed format support
needs_mixing = args.custom_type or args.fallback
needs_mixing = args.custom_type or args.fallback or args.layer_config

if needs_mixing:
# Route through unified path for mixed format support
Expand Down Expand Up @@ -730,9 +725,9 @@ def run_conversion(args):
# Call convert_to_fp8_scaled with explicit args (no **kwargs footgun)
# Determine primary_format for NVFP4/MXFP8 mixed mode (when they fall through here)
primary_format = None
if args.nvfp4 and (args.custom_type or args.fallback):
if args.nvfp4 and (args.custom_type or args.fallback or args.layer_config):
primary_format = "nvfp4"
elif args.mxfp8 and (args.custom_type or args.fallback):
elif args.mxfp8 and (args.custom_type or args.fallback or args.layer_config):
primary_format = "mxfp8"

convert_to_fp8_scaled(
Expand Down
4 changes: 4 additions & 0 deletions convert_to_quant/formats/fp8_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ def get_format_info(fmt: str) -> dict:
layer_format = "fp8"
elif fmt.startswith("int8"):
layer_format = "int8"
elif fmt == "mxfp8":
layer_format = "mxfp8"
elif fmt == "nvfp4":
layer_format = "nvfp4"
else:
layer_format = "fp8" # fallback

Expand Down
19 changes: 10 additions & 9 deletions tests/test_filter_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- Excluded layers (via filter "exclude", "highprec", or "remove") are handled correctly
- 1D/3D/4D tensors are never quantized regardless of layer name
- AVOID_KEY_NAMES (norm, bias, embed_tokens…) are skipped in nvfp4/mxfp8 paths
- TEXT_MODEL_ALIASES (qwen35 -> generic_text) propagate correctly
- filter extraction uses only explicitly enabled filter flags
- Quantized layers produce .weight_scale and .comfy_quant
- Skipped layers do NOT produce .weight_scale / .comfy_quant / .input_scale
- "remove" key deletes layers entirely from output (t5xxl decoder removal)
Expand Down Expand Up @@ -385,11 +385,11 @@ def test_mxfp8_qwen35_flag_skips_excluded_layers(self):
self.assertIn(f"{base}.weight", out)

# ------------------------------------------------------------------
# 6. TEXT_MODEL_ALIASES: qwen35 via extract_filter_flags injects generic_text
# 6. extract_filter_flags preserves only explicit filter flags
# ------------------------------------------------------------------

def test_extract_filter_flags_qwen35_injects_generic_text(self):
"""extract_filter_flags must set generic_text=True when qwen35 is set."""
def test_extract_filter_flags_qwen35_does_not_inject_generic_text(self):
"""extract_filter_flags must not add generic_text when only qwen35 is set."""
# Build a fake args namespace with all MODEL_FILTERS keys set to False
# except qwen35
import types
Expand All @@ -404,24 +404,25 @@ def test_extract_filter_flags_qwen35_injects_generic_text(self):
flags = extract_filter_flags(ns)

self.assertTrue(flags.get("qwen35"), "qwen35 must be True in flags")
self.assertTrue(flags.get("generic_text"), "generic_text must be auto-injected when qwen35 is set")
self.assertNotIn("generic_text", flags, "generic_text must not be auto-injected when qwen35 is set")

def test_extract_filter_flags_no_alias_without_qwen35(self):
"""generic_text must NOT be injected when qwen35 is not set."""
def test_extract_filter_flags_keeps_explicit_generic_text(self):
"""generic_text must be present only when explicitly enabled."""
import types

from convert_to_quant.constants import MODEL_FILTERS

ns = types.SimpleNamespace()
for name in MODEL_FILTERS.keys():
setattr(ns, name, False)
setattr(ns, "generic_text", True)

flags = extract_filter_flags(ns)

self.assertNotIn("generic_text", flags, "generic_text must not appear when no alias is active")
self.assertTrue(flags.get("generic_text"), "generic_text must be kept when explicitly active")

# ------------------------------------------------------------------
# 7. --qwen35 input_scale behavior via generic_text injection (FP8)
# 7. Explicit qwen35 + generic_text input_scale behavior (FP8)
# ------------------------------------------------------------------

def test_fp8_qwen35_generic_text_produces_input_scale(self):
Expand Down