🖥️ Sandboxes: isolated cloud machines on top of Jobs
Sandboxes are isolated cloud machines you can spin up in seconds, run commands in with live-streamed output, and move files in and out of — all from Python or the CLI. They are built entirely on top of Jobs: under the hood a sandbox is just a Job running a tiny static server, so any Docker image with /bin/sh works and it inherits Jobs' billing, hardware flavors, and namespace permissions for free. Two flavors are available: Sandbox.create for a dedicated VM (GPU workloads, untrusted code, full isolation) and SandboxPool to pack many cheap CPU sandboxes into a few shared host VMs for fan-out workloads like RL rollouts. This release also adds background processes (sbx.run(..., background=True) / hf sandbox spawn) and a port proxy (Sandbox.proxy_url_for) so you can reach a server running inside a sandbox from the outside over HTTP or WebSocket.
from huggingface_hub import Sandbox
with Sandbox.create(image="python:3.12") as sbx: # ready in ~6s
sbx.files.write("/app/main.py", "print(40 + 2)")
print(sbx.run("python /app/main.py").stdout) # 42# Create, run, copy files, and terminate from the terminal
hf sandbox create
hf sandbox exec <id> -- python -c "print('hi')"
hf sandbox cp data.csv <id>:/data/data.csv
hf sandbox kill <id>- [Sandbox] Add Sandbox API and
hf sandboxCLI on top of Jobs by @Wauplin in #4350 - [Sandbox] Background processes + proxy to reach in-sandbox servers by @Wauplin in #4444
📚 Documentation: Sandboxes guide, Sandbox reference
⚡ Faster snapshot downloads with a tree cache
snapshot_download now caches a repository's file listing on disk under a new trees/ folder, so re-downloading a commit that's already cached costs a single network call — resolving the branch or tag to a commit hash — instead of one metadata request per file. The listing is immutable per commit and shared by both snapshot_download and hf_hub_download; for Xet-enabled files it also skips the per-file HEAD /resolve request entirely, rebuilding the metadata from the cached listing. As a deliberate side effect of the completeness check, when the Hub can't be reached and the local snapshot is missing requested files, snapshot_download now raises IncompleteSnapshotError instead of silently returning a partial folder.
📚 Documentation: Manage your cache
🛠️ CLI rebuilt on Click (drops Typer)
The entire hf CLI now runs on a small in-house layer over Click 8.x instead of Typer, which had vendored Click in a way that broke the CLI's custom help rendering, error enrichment, and shell completion — and forced capping typer<0.26. The migration preserves existing behavior: --help output is byte-identical, the generated cli.md reference is unchanged apart from a header comment, and shell completion now uses Click's native completion. The public typer_factory helper is kept so downstream libraries like transformers that register their own commands keep working.
- [CLI] Add a minimal Click framework for the CLI by @hanouticelina in #4462
💔 Breaking Change
- [Upload] Deprecate upload_large_folder (API + CLI) by @Wauplin in #4414 —
upload_large_folderandhf upload-large-folderare now deprecated in favor ofupload_folder/hf upload, which handle very large and resumable uploads out of the box. - Make filter_repo_objects pattern matching case-sensitive on all platforms by @Sreekant13 in #4435 —
allow_patterns/ignore_patternsnow match case-sensitively on every OS (aligned with case-sensitive Hub paths). On Windows this is a behavior change: patterns like*.PDFno longer matchfile.pdf. - [Inference Providers] Remove dead inference providers by @hanouticelina in #4447 — removes six providers no longer routed by the Hub (
black-forest-labs,clarifai,hyperbolic,nebius,nvidia,sambanova) — docs
🖥️ CLI
- [CLI] Add
hf discussions editby @Wauplin in #4415 — docs - [CLI] hf cache: surface & prune incomplete downloads by @Wauplin in #4416 —
hf cache lsnow flags leftover.incompletefiles andhf cache pruneremoves them automatically — docs - [CLI] Point users at hf jobs wait in job hints by @davanstrien in #4429
- [CLI] Expose
outsingleton publicly + addout.logmethod by @Wauplin in #4471
🤖 Inference
📊 Jobs
- [Jobs] Add sync_job_volume helper and local paths in hf jobs -v by @Wauplin in #4346 — sync a local directory to a
jobs-artifactsbucket and mount it;-vaccepts local directories inhf jobs run/uv run(and scheduled variants) — docs - [Jobs] Add
hf jobs scheduled trigger ...to trigger scheduled jobs on demand by @Wauplin in #4459 — docs
🔧 Other QoL Improvements
- [Http] Support standard Retry-After header in http_backoff by @Wauplin in #4460 —
http_backoffnow honors the standardRetry-Afterheader (delay-seconds form); HF rate-limit headers still take precedence when present. - Expose base_model filter param on get_dataset_leaderboard by @NathanHB in #4474 — pass
base_model=Falsetoget_dataset_leaderboardto include fine-tuned/derivative repos that declare a parent model.
📖 Documentation
- Docs: Jobs are no longer Pro-only — update availability note in jobs guide by @davanstrien in #4479 — docs
- Fix misleading max_retries docstring in http_backoff by @Sreekant13 in #4436
- fix docstring by @hunterhogan in #4443
🐛 Bug and typo fixes
- [CLI] Fix escaped backslash handling in .env value parsing by @sarathfrancis90 in #4413
- [URIs] Percent-encode the revision in HfUri.to_url by @sarathfrancis90 in #4418
- Accept two-letter byte units (KB/MB/GB/TB/PB) in parse_size by @Sreekant13 in #4468 — documented
hf cache ls --filterthresholds likesize>1GBnow parse instead of raising. - Fix KeyError in get_dataset_leaderboard when entry has no source by @NathanHB in #4473
- Do not suggest reporting if colab vault error by @Wauplin in #4437
- [Build] Include huggingface_hub.templates via find_namespace_packages by @Wauplin in #4438 — model/dataset card templates are now shipped in wheels (previously skipped due to the missing
__init__.py).
🏗️ Internal
- [Tests] Migrate test_hf_api.py from unittest to pytest by @Wauplin in #4407
- Post-release: bump version to 1.22.0.dev0 by @huggingface-hub-bot[bot] in #4410
- [Tests] Migrate everything from unittest to pytest by @Wauplin in #4439
- [Tests] Continue pytest migration/cleaning by @Wauplin in #4453
- [Tests] last time we need to refacto tests by @Wauplin in #4455
- [Fix] fix TestConfigDictNotRequired skipped tests by @Wauplin in #4461
- Bump the actions group across 1 directory with 4 updates by @dependabot[bot] in #4457
- [CLI] Remove module-level Usage command lists from CLI files by @moon-bot-app[bot] in #4472