test: add real-cache offline GGUF integration checks for #7481
Download unsloth/gemma-3-270m-it-bnb-4bit (~430MB) and verify offline snapshot resolution and tokenizer load with network blocked. Full unsloth import tests remain GPU-gated.
This commit is contained in:
parent
d1692a0011
commit
82f5551e1a
2 changed files with 163 additions and 0 deletions
53
tests/saving/run_offline_gguf_integration.py
Normal file
53
tests/saving/run_offline_gguf_integration.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Download real Gemma weights and run offline integration tests for #7481.
|
||||
|
||||
Example:
|
||||
python tests/saving/run_offline_gguf_integration.py
|
||||
python tests/saving/run_offline_gguf_integration.py --download-only
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = "unsloth/gemma-3-270m-it-bnb-4bit"
|
||||
CACHE_ROOT = Path(os.environ.get("HF_HOME", "/tmp/hf_offline_test_cache"))
|
||||
|
||||
|
||||
def download():
|
||||
from huggingface_hub import snapshot_download
|
||||
|
||||
os.environ.setdefault("HF_HOME", str(CACHE_ROOT))
|
||||
path = snapshot_download(REPO, cache_dir=str(CACHE_ROOT / "hub"))
|
||||
print("cached at", path)
|
||||
|
||||
|
||||
def run_tests():
|
||||
os.environ.setdefault("HF_HOME", str(CACHE_ROOT))
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pytest",
|
||||
"tests/saving/test_offline_gguf_vlm_tokenizer_7481.py",
|
||||
"tests/saving/test_offline_gguf_real_cache_integration.py",
|
||||
"-q",
|
||||
]
|
||||
raise SystemExit(subprocess.call(cmd, cwd=str(Path(__file__).resolve().parents[2])))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--download-only", action="store_true")
|
||||
args = parser.parse_args()
|
||||
download()
|
||||
if not args.download_only:
|
||||
run_tests()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
110
tests/saving/test_offline_gguf_real_cache_integration.py
Normal file
110
tests/saving/test_offline_gguf_real_cache_integration.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"""Integration tests for #7481 using real cached Gemma weights.
|
||||
|
||||
Requires a one-time online download:
|
||||
HF_HOME=/tmp/hf_offline_test_cache python -c \\
|
||||
"from huggingface_hub import snapshot_download; snapshot_download('unsloth/gemma-3-270m-it-bnb-4bit', cache_dir='/tmp/hf_offline_test_cache/hub')"
|
||||
|
||||
No GPU. No full unsloth import (CPU-only hosts cannot import the package graph).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import socket
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
REPO = "unsloth/gemma-3-270m-it-bnb-4bit"
|
||||
CACHE_ROOT = Path(os.environ.get("HF_HOME", "/tmp/hf_offline_test_cache"))
|
||||
|
||||
|
||||
def _require_cached_repo():
|
||||
from huggingface_hub import scan_cache_dir
|
||||
|
||||
cache_dir = CACHE_ROOT / "hub"
|
||||
if not cache_dir.exists():
|
||||
pytest.skip(f"cache missing at {cache_dir}; run snapshot_download for {REPO}")
|
||||
repos = [r.repo_id for r in scan_cache_dir(str(cache_dir)).repos]
|
||||
if REPO not in repos:
|
||||
pytest.skip(f"{REPO} not in {cache_dir}")
|
||||
|
||||
|
||||
def _block_network(monkeypatch):
|
||||
def _guard(*args, **kwargs):
|
||||
raise OSError("network blocked for offline integration test")
|
||||
|
||||
monkeypatch.setattr(socket, "socket", _guard)
|
||||
monkeypatch.setattr(socket, "create_connection", _guard)
|
||||
monkeypatch.setattr(socket, "getaddrinfo", _guard)
|
||||
|
||||
|
||||
def _offline_env(monkeypatch):
|
||||
monkeypatch.setenv("HF_HUB_OFFLINE", "1")
|
||||
monkeypatch.setenv("TRANSFORMERS_OFFLINE", "1")
|
||||
monkeypatch.setenv("HF_HOME", str(CACHE_ROOT))
|
||||
|
||||
|
||||
def _resolve_snapshot(cache_dir: Path) -> Path:
|
||||
from huggingface_hub import hf_hub_download
|
||||
|
||||
path = hf_hub_download(
|
||||
REPO,
|
||||
"tokenizer_config.json",
|
||||
cache_dir=str(cache_dir),
|
||||
local_files_only=True,
|
||||
)
|
||||
return Path(path).parent
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_real_cached_snapshot_resolves_offline(monkeypatch):
|
||||
_require_cached_repo()
|
||||
_offline_env(monkeypatch)
|
||||
_block_network(monkeypatch)
|
||||
|
||||
snap = _resolve_snapshot(CACHE_ROOT / "hub")
|
||||
assert (snap / "tokenizer.json").is_file()
|
||||
assert (snap / "tokenizer.model").is_file()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_real_cached_tokenizer_loads_from_snapshot_not_repo_id(monkeypatch):
|
||||
"""Mirrors the #7481 fix: load from snapshot dir, not Hub repo id."""
|
||||
_require_cached_repo()
|
||||
_offline_env(monkeypatch)
|
||||
_block_network(monkeypatch)
|
||||
|
||||
from transformers import PreTrainedTokenizerFast
|
||||
|
||||
snap = _resolve_snapshot(CACHE_ROOT / "hub")
|
||||
tok = PreTrainedTokenizerFast.from_pretrained(str(snap), local_files_only=True)
|
||||
assert tok.vocab_size > 0
|
||||
|
||||
# Repo-id path is what triggered model_info() offline in #7481; snapshot path is the fix.
|
||||
assert str(snap) != REPO
|
||||
assert "/" not in Path(str(snap)).name
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("UNSLOTH_INTEGRATION_IMPORT") != "1",
|
||||
reason="full unsloth import needs GPU host; set UNSLOTH_INTEGRATION_IMPORT=1 to enable",
|
||||
)
|
||||
def test_real_cached_unsloth_helpers_offline(monkeypatch):
|
||||
_require_cached_repo()
|
||||
_offline_env(monkeypatch)
|
||||
_block_network(monkeypatch)
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from unsloth.models.loader_utils import _load_pretrained_tokenizer_fast
|
||||
from unsloth.save import _has_tokenizer_model
|
||||
|
||||
tok = _load_pretrained_tokenizer_fast(
|
||||
REPO,
|
||||
local_files_only=True,
|
||||
cache_dir=str(CACHE_ROOT / "hub"),
|
||||
)
|
||||
assert tok.vocab_size > 0
|
||||
assert _has_tokenizer_model(SimpleNamespace(name_or_path=REPO, tokenizer=None)) is True
|
||||
Loading…
Add table
Add a link
Reference in a new issue