Files
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

276 lines
9.1 KiB
Python

"""CPU-only tests for the GGUF imatrix export option.
Cover imatrix_file resolution (path / *.gguf_file rename / True auto-download with mocked Hub),
the upstream unsloth/<base>-GGUF repo derivation, the conditional IQ-quant gate in save_to_gguf,
and that quantize_gguf / _quantize_q2_k_l actually emit --imatrix. No GPU, no real weights, no
real Hub or llama.cpp - the heavy bits are monkeypatched.
"""
from __future__ import annotations
import inspect
import os
import pytest
import unsloth.save as S
import unsloth_zoo.llama_cpp as L
# The --imatrix wiring lives in unsloth_zoo's quantize_gguf (a companion change). Where the
# installed unsloth_zoo predates it, skip the tests that require it rather than hard-failing CI.
_ZOO_HAS_IMATRIX = "imatrix" in inspect.signature(L.quantize_gguf).parameters
_needs_zoo_imatrix = pytest.mark.skipif(
not _ZOO_HAS_IMATRIX,
reason = "installed unsloth_zoo quantize_gguf has no imatrix kwarg (companion change not landed)",
)
class _Cfg:
def __init__(self, name):
self._name_or_path = name
self.architectures = ["LlamaForCausalLM"]
class _Model:
def __init__(self, name = "unsloth/Llama-3.1-8B-Instruct"):
self.config = _Cfg(name)
self.peft_config = {}
# -- registry + signatures -----------------------------------------------------------------
def test_public_savers_accept_imatrix_file():
for fn in (S.unsloth_save_pretrained_gguf, S.unsloth_push_to_hub_gguf):
assert "imatrix_file" in inspect.signature(fn).parameters, fn.__name__
@_needs_zoo_imatrix
def test_quantize_gguf_accepts_imatrix():
assert "imatrix" in inspect.signature(L.quantize_gguf).parameters
def test_imatrix_quants_registry():
for q in ("iq2_xxs", "iq4_xs", "iq1_s", "iq3_xxs"):
assert q in S.IMATRIX_QUANTS
assert q not in S.ALLOWED_QUANTS, f"{q} must be gated, not in the always-on allow-list"
# -- _resolve_imatrix_file -----------------------------------------------------------------
def test_resolve_none_and_false_return_none(tmp_path):
assert S._resolve_imatrix_file(_Model(), None, None, str(tmp_path)) is None
assert S._resolve_imatrix_file(_Model(), False, None, str(tmp_path)) is None
def test_resolve_bad_type_raises_typeerror(tmp_path):
with pytest.raises(TypeError):
S._resolve_imatrix_file(_Model(), 123, None, str(tmp_path))
def test_resolve_missing_path_raises(tmp_path):
with pytest.raises(FileNotFoundError):
S._resolve_imatrix_file(_Model(), str(tmp_path / "nope.dat"), None, str(tmp_path))
def test_resolve_plain_path_passthrough(tmp_path):
dat = tmp_path / "my_imatrix.dat"
dat.write_bytes(b"x" * 32)
assert S._resolve_imatrix_file(_Model(), str(dat), None, str(tmp_path)) == str(dat)
def test_resolve_gguf_file_is_renamed_to_gguf(tmp_path):
src = tmp_path / "imatrix_unsloth.gguf_file"
src.write_bytes(b"x" * 32)
dest = tmp_path / "export"
out = S._resolve_imatrix_file(_Model(), str(src), None, str(dest))
assert out.endswith(".gguf") and not out.endswith(".gguf_file")
assert os.path.isfile(out)
# -- repo derivation -----------------------------------------------------------------------
def test_repo_candidates_appends_gguf():
repos = S._gguf_repo_candidates(_Model("unsloth/Llama-3.1-8B-Instruct"))
assert "unsloth/Llama-3.1-8B-Instruct-GGUF" in repos
def test_repo_candidates_maps_official_base_to_unsloth_org():
# The upstream imatrix only lives in unsloth/<base>-GGUF, so an official base id must map onto
# the unsloth org rather than deriving a non-existent meta-llama/...-GGUF repo.
repos = S._gguf_repo_candidates(_Model("meta-llama/Llama-3.1-8B-Instruct"))
assert "unsloth/Llama-3.1-8B-Instruct-GGUF" in repos
assert not any(r.startswith("meta-llama/") for r in repos)
def test_repo_candidates_keeps_existing_gguf_suffix():
repos = S._gguf_repo_candidates(_Model("unsloth/Qwen3.6-35B-A3B-GGUF"))
assert repos == ["unsloth/Qwen3.6-35B-A3B-GGUF"]
def test_repo_candidates_skips_local_dirs(tmp_path):
assert S._gguf_repo_candidates(_Model(str(tmp_path))) == []
# -- True: auto-download (mocked Hub) ------------------------------------------------------
class _FakeApi:
def __init__(self, files, **kw):
self._files = files
def list_repo_files(self, repo_id):
return list(self._files.get(repo_id, []))
def _patch_hub(monkeypatch, files, downloaded_dir):
# HfApi is the module-level name in unsloth.save; hf_hub_download is imported locally inside
# the helper, so patch it on huggingface_hub. Both must be patched to stay fully offline.
monkeypatch.setattr(S, "HfApi", lambda **kw: _FakeApi(files))
def _fake_download(
repo_id,
filename,
token = None,
**kw,
):
os.makedirs(downloaded_dir, exist_ok = True)
path = os.path.join(downloaded_dir, filename)
with open(path, "wb") as f:
f.write(b"imatrix-bytes")
return path
import huggingface_hub
monkeypatch.setattr(huggingface_hub, "hf_hub_download", _fake_download)
def test_resolve_true_prefers_dat(monkeypatch, tmp_path):
cache = tmp_path / "cache"
files = {
"unsloth/Llama-3.1-8B-Instruct-GGUF": [
"imatrix_unsloth.dat",
"imatrix_unsloth.gguf_file",
"model.Q4_K_M.gguf",
]
}
_patch_hub(monkeypatch, files, str(cache))
out = S._resolve_imatrix_file(_Model(), True, "tok", str(tmp_path / "dest"))
assert os.path.basename(out) == "imatrix_unsloth.dat"
# downloaded into the caller dest, not left only in the (fake) cache
assert os.path.dirname(out) == str(tmp_path / "dest")
def test_resolve_true_downloads_gguf_file_and_renames(monkeypatch, tmp_path):
cache = tmp_path / "cache"
files = {"unsloth/Llama-3.1-8B-Instruct-GGUF": ["imatrix_unsloth.gguf_file"]}
_patch_hub(monkeypatch, files, str(cache))
out = S._resolve_imatrix_file(_Model(), True, "tok", str(tmp_path / "dest"))
assert os.path.basename(out) == "imatrix_unsloth.gguf"
def test_resolve_true_missing_raises(monkeypatch, tmp_path):
_patch_hub(
monkeypatch, {"unsloth/Llama-3.1-8B-Instruct-GGUF": ["model.Q4_K_M.gguf"]}, str(tmp_path)
)
with pytest.raises(RuntimeError) as e:
S._resolve_imatrix_file(_Model(), True, "tok", str(tmp_path / "dest"))
assert "imatrix" in str(e.value).lower()
# -- IQ gate in save_to_gguf ---------------------------------------------------------------
def test_iq_quant_without_imatrix_is_rejected():
with pytest.raises(RuntimeError) as e:
S.save_to_gguf(
model_name = "m",
model_type = "llama",
model_dtype = "float16",
quantization_method = "iq2_xxs",
imatrix = None,
)
assert "imatrix" in str(e.value).lower()
def test_unknown_quant_is_rejected():
with pytest.raises(RuntimeError):
S.save_to_gguf(
model_name = "m",
model_type = "llama",
model_dtype = "float16",
quantization_method = "totally_bogus",
imatrix = None,
)
# -- --imatrix actually reaches llama-quantize ---------------------------------------------
@_needs_zoo_imatrix
def test_quantize_gguf_emits_imatrix_flag(monkeypatch, tmp_path):
captured = {}
def _fake_run(command, *a, **kw):
captured["command"] = command
# llama-quantize would write the output; emulate so the existence check passes.
out = command.split()[-2] if False else None
# output_gguf is the 2nd-to-last token before quant_type/threads; just create it.
with open(tmp_path / "out.gguf", "wb") as f:
f.write(b"GGUF")
class R:
returncode = 0
stdout = ""
return R()
import shlex
monkeypatch.setattr(L.subprocess, "run", _fake_run)
imat = str(tmp_path / "imatrix it.dat") # space in path -> must be shell-quoted
with open(imat, "wb") as f: # quantize_gguf validates the imatrix exists before running
f.write(b"\x00")
L.quantize_gguf(
input_gguf = str(tmp_path / "in.gguf"),
output_gguf = str(tmp_path / "out.gguf"),
quant_type = "iq4_xs",
quantizer_location = "llama-quantize",
n_threads = 4,
imatrix = imat,
print_output = False,
)
cmd = captured["command"]
assert "--imatrix" in cmd
assert "iq4_xs" in cmd
# the path with a space must appear shell-quoted (shlex.quote), never bare
assert f"--imatrix {shlex.quote(imat)}" in cmd
def test_quantize_gguf_no_imatrix_has_no_flag(monkeypatch, tmp_path):
captured = {}
def _fake_run(command, *a, **kw):
captured["command"] = command
with open(tmp_path / "out.gguf", "wb") as f:
f.write(b"GGUF")
class R:
returncode = 0
stdout = ""
return R()
monkeypatch.setattr(L.subprocess, "run", _fake_run)
L.quantize_gguf(
input_gguf = str(tmp_path / "in.gguf"),
output_gguf = str(tmp_path / "out.gguf"),
quant_type = "q4_k_m",
quantizer_location = "llama-quantize",
n_threads = 4,
print_output = False,
)
assert "--imatrix" not in captured["command"]