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
146 lines
4.6 KiB
Python
146 lines
4.6 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
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from hub.utils.hf_cache_state import iter_repo_cache_dirs
|
|
|
|
|
|
TRAINING_DATA_EXTS = (".parquet", ".json", ".jsonl", ".csv")
|
|
|
|
|
|
def _rel_lower(snapshot: Path, path: Path) -> str:
|
|
return path.relative_to(snapshot).as_posix().lower()
|
|
|
|
|
|
_SPLIT_ALIASES = {
|
|
"validation": frozenset({"validation", "valid", "val"}),
|
|
"valid": frozenset({"validation", "valid", "val"}),
|
|
"val": frozenset({"validation", "valid", "val"}),
|
|
"eval": frozenset({"eval", "validation", "valid", "val"}),
|
|
}
|
|
|
|
|
|
def _label_tokens(text: str) -> set[str]:
|
|
return {token for token in re.split(r"[^a-z0-9]+", text.lower()) if token}
|
|
|
|
|
|
def split_label_matches(text: str, split: str) -> bool:
|
|
"""Match a split name against a file path's tokens, expanding split aliases
|
|
(validation/valid/val, eval) so cached and remote selection agree."""
|
|
normalized = split.strip().lower()
|
|
if not normalized:
|
|
return False
|
|
labels = _SPLIT_ALIASES.get(normalized, frozenset({normalized}))
|
|
return bool(labels.intersection(_label_tokens(text)))
|
|
|
|
|
|
def _matches_label(snapshot: Path, path: Path, label: str) -> bool:
|
|
label = label.strip().lower()
|
|
if not label:
|
|
return False
|
|
rel = _rel_lower(snapshot, path)
|
|
tokens = [token for token in re.split(r"[^a-z0-9]+", rel) if token]
|
|
if label in tokens:
|
|
return True
|
|
if label in {"train", "test", "validation", "valid", "val", "eval"}:
|
|
return False
|
|
return label in rel
|
|
|
|
|
|
def dataset_snapshot_from_cache_path(local_path: Optional[str], repo_id: str) -> Optional[Path]:
|
|
if not local_path or not repo_id:
|
|
return None
|
|
try:
|
|
root = Path(local_path).expanduser()
|
|
if not root.exists():
|
|
return None
|
|
expected_repo_dir = f"datasets--{repo_id.replace('/', '--')}".lower()
|
|
if expected_repo_dir not in {part.lower() for part in root.parts}:
|
|
return None
|
|
if root.is_dir() and root.parent.name == "snapshots":
|
|
return root.resolve()
|
|
snapshots = root / "snapshots" if root.is_dir() else None
|
|
if snapshots is None or not snapshots.is_dir():
|
|
return None
|
|
candidates = [p for p in snapshots.iterdir() if p.is_dir()]
|
|
if not candidates:
|
|
return None
|
|
candidates.sort(
|
|
key = lambda path: path.stat().st_mtime if path.exists() else 0,
|
|
reverse = True,
|
|
)
|
|
return candidates[0].resolve()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def latest_cached_dataset_snapshot(
|
|
repo_id: str, local_path: Optional[str] = None
|
|
) -> Optional[Path]:
|
|
local_snapshot = dataset_snapshot_from_cache_path(local_path, repo_id)
|
|
if local_snapshot is not None:
|
|
return local_snapshot
|
|
|
|
newest: Optional[Path] = None
|
|
newest_mtime = -1.0
|
|
for entry in iter_repo_cache_dirs("dataset", repo_id):
|
|
snapshots = entry / "snapshots"
|
|
if not snapshots.is_dir():
|
|
continue
|
|
try:
|
|
candidates = [s for s in snapshots.iterdir() if s.is_dir()]
|
|
except OSError:
|
|
continue
|
|
for snap in candidates:
|
|
try:
|
|
mtime = snap.stat().st_mtime
|
|
except OSError:
|
|
continue
|
|
if mtime > newest_mtime:
|
|
newest = snap
|
|
newest_mtime = mtime
|
|
return newest
|
|
|
|
|
|
def cached_dataset_candidates(
|
|
snapshot: Path,
|
|
*,
|
|
subset: Optional[str],
|
|
train_split: str,
|
|
extensions: tuple[str, ...],
|
|
preferred_extensions: tuple[str, ...] = TRAINING_DATA_EXTS,
|
|
) -> list[Path]:
|
|
try:
|
|
files = [
|
|
p for p in snapshot.rglob("*") if p.is_file() and p.name.lower().endswith(extensions)
|
|
]
|
|
except OSError:
|
|
return []
|
|
if not files:
|
|
return []
|
|
|
|
subset_lower = subset.lower() if subset else ""
|
|
split_lower = train_split.lower()
|
|
|
|
def score(path: Path) -> tuple[int, int, str]:
|
|
rel = _rel_lower(snapshot, path)
|
|
subset_match = bool(subset_lower and _matches_label(snapshot, path, subset_lower))
|
|
split_match = bool(split_lower and split_label_matches(rel, split_lower))
|
|
location_rank = 3
|
|
if split_match and (not subset_lower or subset_match):
|
|
location_rank = 0
|
|
elif split_match:
|
|
location_rank = 1
|
|
elif subset_match:
|
|
location_rank = 2
|
|
return (
|
|
0 if path.name.lower().endswith(preferred_extensions) else 1,
|
|
location_rank,
|
|
rel,
|
|
)
|
|
|
|
return sorted(files, key = score)
|