diff --git a/studio/backend/hub/tests/test_model_services.py b/studio/backend/hub/tests/test_model_services.py index fa5862a13c..3641360e59 100644 --- a/studio/backend/hub/tests/test_model_services.py +++ b/studio/backend/hub/tests/test_model_services.py @@ -85,6 +85,11 @@ class TestExtractQuantToken: def test_ud_prefix_preserved(self): assert gguf.extract_quant_token("Foo-BF16-UD-Q4_K_XL.gguf") == "UD-Q4_K_XL" + def test_packed_and_grouped_quant_variants_do_not_collapse(self): + assert gguf.extract_quant_token("Ternary-Bonsai-1.7B-PQ2_0.gguf") == "PQ2_0" + assert gguf.extract_quant_token("Ternary-Bonsai-1.7B-Q2_0.gguf") == "Q2_0" + assert gguf.extract_quant_token("Ternary-Bonsai-1.7B-Q2_0_g64.gguf") == "Q2_0_g64" + def test_precision_infix_variants_do_not_collapse(self): labels = { gguf.extract_quant_label("Foo-BF16-Q4_K_M.gguf"), @@ -1086,6 +1091,21 @@ def test_gguf_variant_requirements_include_split_files_and_preferred_mmproj(): ) +def test_gguf_variant_requirements_keep_packed_q2_files_separate(): + requirements = gguf_variants._build_gguf_variant_requirements( + [ + _sibling("Ternary-Bonsai-1.7B-PQ2_0.gguf", 10, "pq"), + _sibling("Ternary-Bonsai-1.7B-Q2_0.gguf", 20, "q2"), + _sibling("Ternary-Bonsai-1.7B-Q2_0_g64.gguf", 30, "q2g64"), + ] + ) + + assert set(requirements) == {"pq2_0", "q2_0", "q2_0_g64"} + assert requirements["pq2_0"].target_filenames == ("Ternary-Bonsai-1.7B-PQ2_0.gguf",) + assert requirements["q2_0"].target_filenames == ("Ternary-Bonsai-1.7B-Q2_0.gguf",) + assert requirements["q2_0_g64"].target_filenames == ("Ternary-Bonsai-1.7B-Q2_0_g64.gguf",) + + def test_gguf_variant_requirements_skip_big_endian_sibling(): requirements = gguf_variants._build_gguf_variant_requirements( [ diff --git a/studio/backend/hub/utils/gguf.py b/studio/backend/hub/utils/gguf.py index eb768db5d6..94c4338d71 100644 --- a/studio/backend/hub/utils/gguf.py +++ b/studio/backend/hub/utils/gguf.py @@ -72,7 +72,7 @@ _GGUF_QUANT_RE = re.compile( r"|IQ[0-9]+_[A-Z]+(?:_[A-Z0-9]+)?" r"|TQ[0-9]+_[0-9]+" r"|Q[0-9]+_K_[A-Z]+" - r"|Q[0-9]+_[0-9]+" + r"|P?Q[0-9]+_[0-9]+(?:_G[0-9]+)?" r"|Q[0-9]+_K" r"|BF16|F16|F32)", re.IGNORECASE, diff --git a/studio/backend/utils/models/model_config.py b/studio/backend/utils/models/model_config.py index 6270d9e03f..46c5ec34f2 100644 --- a/studio/backend/utils/models/model_config.py +++ b/studio/backend/utils/models/model_config.py @@ -1630,7 +1630,7 @@ def _extract_quant_label(filename: str) -> str: r"|IQ[0-9]+_[A-Z]+(?:_[A-Z0-9]+)?" # IQ variants: IQ4_XS, IQ4_NL, IQ1_S r"|TQ[0-9]+_[0-9]+" # Ternary quant: TQ1_0, TQ2_0 r"|Q[0-9]+_K_[A-Z]+" # K-quant: Q4_K_M, Q3_K_S - r"|Q[0-9]+_[0-9]+" # Standard: Q8_0, Q5_1 + r"|P?Q[0-9]+_[0-9]+(?:_G[0-9]+)?" # Standard/Packed: Q8_0, PQ2_0, Q2_0_g64 r"|Q[0-9]+_K" # Short K-quant: Q6_K r"|BF16|F16|F32)" # Full precision # Optional bits-per-weight modifier so repos that ship multiple