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
118 lines
3.9 KiB
Python
118 lines
3.9 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""Tests for local GGUF ``model_format`` classification (PR #6364 follow-up).
|
|
|
|
Suffixless GGUF folders (custom folders / LM Studio) carry no ``-GGUF`` name
|
|
hint, so the scanners must surface ``model_format = "gguf"`` for the UI to route
|
|
them through the GGUF load path. The rule, shared by ``_dir_model_format`` and
|
|
``_scan_models_dir``: a directory is GGUF-format when it holds ``.gguf`` files
|
|
and no non-GGUF weights (``.safetensors`` / ``.bin``); a stray ``config.json``
|
|
must not disqualify it.
|
|
|
|
No GPU/network: only file names and sizes are inspected.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
# Keep runnable without optional logging deps (mirrors the sibling tests).
|
|
if "structlog" not in sys.modules:
|
|
|
|
class _DummyLogger:
|
|
def __getattr__(self, _name):
|
|
return lambda *args, **kwargs: None
|
|
|
|
sys.modules["structlog"] = types.SimpleNamespace(
|
|
BoundLogger = _DummyLogger,
|
|
get_logger = lambda *args, **kwargs: _DummyLogger(),
|
|
)
|
|
|
|
import routes.models as models_route
|
|
|
|
|
|
def _touch(path: Path) -> Path:
|
|
path.parent.mkdir(parents = True, exist_ok = True)
|
|
path.write_bytes(b"\0")
|
|
return path
|
|
|
|
|
|
def test_dir_model_format_gguf_only(tmp_path):
|
|
d = tmp_path / "model"
|
|
_touch(d / "model-Q4_K_M.gguf")
|
|
assert models_route._dir_model_format(d) == "gguf"
|
|
|
|
|
|
def test_dir_model_format_gguf_with_config_is_still_gguf(tmp_path):
|
|
# A config.json alongside the .gguf must not flip it to non-GGUF.
|
|
d = tmp_path / "model"
|
|
_touch(d / "config.json")
|
|
_touch(d / "model-Q4_K_M.gguf")
|
|
assert models_route._dir_model_format(d) == "gguf"
|
|
|
|
|
|
def test_dir_model_format_mixed_weights_is_not_gguf(tmp_path):
|
|
# Real safetensors weights present -> not a GGUF folder.
|
|
d = tmp_path / "model"
|
|
_touch(d / "model.safetensors")
|
|
_touch(d / "model-Q4_K_M.gguf")
|
|
assert models_route._dir_model_format(d) is None
|
|
|
|
|
|
def test_dir_model_format_no_gguf(tmp_path):
|
|
d = tmp_path / "model"
|
|
_touch(d / "config.json")
|
|
_touch(d / "model.safetensors")
|
|
assert models_route._dir_model_format(d) is None
|
|
|
|
|
|
def test_dir_model_format_ignores_tokenizer_bin(tmp_path):
|
|
# A companion tokenizer.bin is not a weight file, so a GGUF folder shipping
|
|
# one is still GGUF (not misread as a plain .bin checkpoint).
|
|
d = tmp_path / "model"
|
|
_touch(d / "tokenizer.bin")
|
|
_touch(d / "model-Q4_K_M.gguf")
|
|
assert models_route._dir_model_format(d) == "gguf"
|
|
|
|
|
|
def test_dir_model_format_weight_bin_is_not_gguf(tmp_path):
|
|
# A real PyTorch weight .bin alongside a .gguf means mixed weights -> None.
|
|
d = tmp_path / "model"
|
|
_touch(d / "pytorch_model.bin")
|
|
_touch(d / "model-Q4_K_M.gguf")
|
|
assert models_route._dir_model_format(d) is None
|
|
|
|
|
|
def test_scan_models_dir_classifies_gguf_with_config(tmp_path):
|
|
root = tmp_path / "models"
|
|
# GGUF repo that also ships a config.json (the regression case).
|
|
_touch(root / "gguf_repo" / "config.json")
|
|
_touch(root / "gguf_repo" / "model-Q4_K_M.gguf")
|
|
# A plain safetensors checkpoint stays non-GGUF.
|
|
_touch(root / "st_repo" / "config.json")
|
|
_touch(root / "st_repo" / "model.safetensors")
|
|
# A standalone .gguf file is GGUF.
|
|
_touch(root / "loose.gguf")
|
|
|
|
fmt = {Path(m.path).name: m.model_format for m in models_route._scan_models_dir(root)}
|
|
|
|
assert fmt["gguf_repo"] == "gguf"
|
|
assert fmt["st_repo"] is None
|
|
assert fmt["loose.gguf"] == "gguf"
|
|
|
|
|
|
def test_scan_models_dir_classifies_root_gguf_with_config(tmp_path):
|
|
# Custom scan folders can point directly at a GGUF repo, not only at a
|
|
# parent directory that contains model repos.
|
|
root = tmp_path / "SuffixlessRepo"
|
|
_touch(root / "config.json")
|
|
_touch(root / "model-Q4_K_M.gguf")
|
|
|
|
[row] = models_route._scan_models_dir(root)
|
|
|
|
assert row.path == str(root)
|
|
assert row.model_format == "gguf"
|