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
20 changes: 18 additions & 2 deletions routes/cookbook_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def _cached_model_scan_script(model_dirs: list[str] | None = None, add_hf_cache:
Allows for an additional HuggingFace cache path to be scanned (i.e. Windows HF cache for local WSL envs.)
"""
lines = [
"import json, os, re, shutil, subprocess, urllib.request",
"import json, os, re, shutil, subprocess, sys, urllib.request",
"models = []",
"seen = set()",
"BLOCKED_ROOTS = ('/sys', '/proc', '/dev', '/run', '/var/run')",
Expand Down Expand Up @@ -488,7 +488,23 @@ def _cached_model_scan_script(model_dirs: list[str] | None = None, add_hf_cache:
" add(os.environ.get('HUGGINGFACE_HUB_CACHE'))",
" hf_home = os.environ.get('HF_HOME')",
" if hf_home: add(os.path.join(hf_home, 'hub'))",
" add('~/.cache/huggingface/hub')",
" # Best-effort: ask huggingface_hub for its own resolved cache.",
" # Its defaults are platform-aware - macOS uses ~/Library/Caches/huggingface",
" # (NOT ~/.cache/...), which is what issue #5978 reported as missing. Falls",
" # back silently so the scanner still runs on remote hosts without the lib.",
" try:",
" import huggingface_hub",
" _c = getattr(huggingface_hub, 'constants', None)",
" _hc = getattr(_c, 'HF_HUB_CACHE', None) or getattr(_c, 'hf_hub_cache', None)",
" if _hc: add(_hc)",
" except Exception:",
" pass",
" # Platform-aware hard-coded fallback (covers hosts lacking huggingface_hub",
" # so the cache is still discoverable on macOS even without the library).",
" if sys.platform == 'darwin':",
" add('~/Library/Caches/huggingface/hub')",
" else:",
" add('~/.cache/huggingface/hub')",
" # Docker images mount ./data/huggingface at /app/.cache/huggingface.",
" # When HOME is /root, expanduser() misses that persisted cache.",
" add('/app/.cache/huggingface/hub')",
Expand Down
67 changes: 66 additions & 1 deletion tests/test_cookbook_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,72 @@ def test_validate_serve_cmd_rejects_unrelated_subshell_pipelines():
(
"llama-server --model model.gguf "
"--mmproj \"$(find '/app/models' -iname '*.gguf' 2>/dev/null | sort | head -1)\""
),
),
]:
with pytest.raises(HTTPException):
_validate_serve_cmd(cmd)


# ----- Issue #5978: macOS HF cache discovery -----
# huggingface_hub stores downloads under ~/Library/Caches/huggingface/hub on
# macOS, but the scanner only knew ~/.cache/... (Linux) and never consulted the
# library, so macOS caches were invisible unless the user manually exported
# HF_HOME. The two tests below pin the fix.


def _exec_hf_cache_paths_def():
"""Recover the shipped `hf_cache_paths()` def out of the generated scanner

and return it as a callable for behavioral testing.

Why exec-and-call instead of reaching the GET /api/model/cached route:
`_cached_model_scan_script` emits a standalone source string intended to
run via `python -` / `ssh host "python -"`; exercising it through the route
would reconstruct a Request + ServeRequest instead of testing the real
emission point. Extracting the one def (by stable delimiters, no regex)
and calling it pins exactly what ships (narrow, documented exception per
tests/TESTING_STANDARD.md).
"""
script = _cached_model_scan_script()
start = script.index("def hf_cache_paths():")
end = script.index(" return candidates", start) + len(" return candidates")
ns: dict = {"os": os, "sys": sys}
exec(script[start:end] + "\n", ns)
return ns["hf_cache_paths"]


def test_hf_cache_paths_includes_macos_library_caches_under_darwin(monkeypatch):
fn = _exec_hf_cache_paths_def()
monkeypatch.setattr("sys.platform", "darwin")
cands = fn()
assert any("Library/Caches/huggingface" in c for c in cands), cands


def test_hf_cache_paths_includes_linux_dotcache_under_linux(monkeypatch):
fn = _exec_hf_cache_paths_def()
monkeypatch.setattr("sys.platform", "linux")
cands = fn()
assert any(".cache/huggingface/hub" in c for c in cands), cands


def test_cached_model_scan_finds_model_at_platform_default_cache(tmp_path):
"""End-to-end: the platform-default cache location (Linux ~/.cache here) must
be scanned with no model_dir / HF_HOME override — the #5978 scenario."""
cache = tmp_path / "home" / "tester" / ".cache" / "huggingface" / "hub"
snap = cache / "models--acme--cached-7b" / "snapshots" / "sha1"
snap.mkdir(parents=True)
(snap / "model-q4.gguf").write_bytes(b"gguf")

env = {k: v for k, v in os.environ.items()
if k not in ("HUGGINGFACE_HUB_CACHE", "HF_HOME")}
env["HOME"] = str(tmp_path / "home" / "tester")

scan_py = tmp_path / "scan_cache.py"
scan_py.write_text(_cached_model_scan_script(), encoding="utf-8")
proc = subprocess.run(
[sys.executable, str(scan_py)],
env=env, capture_output=True, text=True, timeout=30,
)
assert proc.returncode == 0, proc.stderr
models = json.loads(proc.stdout)
assert "acme/cached-7b" in {m["repo_id"] for m in models}, models
Loading