Skip to content

Releases: huggingface/huggingface_hub

[v1.28.0] Hardware discovery and managed engine images for Inference Endpoints and more

Choose a tag to compare

@github-actions github-actions released this 18 Aug 12:03

🔎 Discover deployable hardware with hf endpoints hardware

Deploying an Inference Endpoint requires five hardware flags (--vendor, --region, --accelerator, --instance-type, --instance-size) whose valid values depend on each other, and until now there was no way to learn them from the CLI. The new hf endpoints hardware command lists the valid combinations along with the price per replica per hour and your namespace's accelerator quota, filtered by default to the hardware you can deploy on right now. The same data is available in the SDK via list_inference_endpoints_hardware(), which flattens the API response into InferenceEndpointHardware objects you can filter programmatically.

>>> hf endpoints hardware --vendor aws --region eu-west-1
VENDOR REGION    ACCELERATOR INSTANCE_TYPE INSTANCE_SIZE MEMORY_GB GPU_MEMORY_GB PRICE_PER_HOUR QUOTA STATUS
------ --------- ----------- ------------- ------------- --------- ------------- -------------- ----- ---------
aws    eu-west-1 cpu         intel-spr     x1                  2.0                        0.033 0/60  available
aws    eu-west-1 cpu         intel-spr     x2                  4.0                        0.067 0/60  available
aws    eu-west-1 gpu         nvidia-a10g   x1                 30.0            24            1.0 0/16  available
aws    eu-west-1 gpu         nvidia-t4     x1                 15.0            16            0.5 1/30  available
  • [Inference Endpoints] Add hf endpoints hardware to list available instances by @hanouticelina in #4672

🚀 Managed engine images and multi-accelerator parallelism for Inference Endpoints

custom_image now accepts the engine-specific container types supported by the API: key the dictionary with the engine name (vLLM, sGLang, tgi, tei, llamacpp, hfServe, ...) instead of leaving it flat, and each engine takes the usual container fields plus its own tuning options. Any dict without a top-level url is forwarded to the API untouched, so engines added to the API later will work without upgrading huggingface_hub, and update_inference_endpoint now handles the same payload shapes as create_inference_endpoint. On the CLI, hf endpoints deploy and hf endpoints update gain --engine, --tensor-parallel-size and --data-parallel-size, and update also accepts --custom-image, --health-route and --port. This matters because vLLM and SGLang default to a single accelerator while an endpoint is allocated every accelerator of its instance — the API now rejects that misconfiguration, and these flags are how you set things right.

$ hf endpoints deploy gpt-oss-120b-vllm --repo openai/gpt-oss-120b --framework custom \
    --accelerator gpu --instance-size x8 --instance-type nvidia-h200 --region us-east-1 --vendor aws \
    --engine vllm --custom-image vllm/vllm-openai:v0.23.0 --tensor-parallel-size 8

# Retune a running endpoint
$ hf endpoints update gpt-oss-120b-vllm --tensor-parallel-size 4 --data-parallel-size 2

💔 Breaking change: huggingface_hub.constants.INFERENCE_ENDPOINT_IMAGE_KEYS is removed. It was never exported at the package root nor documented, but code reading it directly will now get an AttributeError.

  • [Inference Endpoints] Support managed engine images in custom_image by @hanouticelina in #4671
  • [CLI] Add --tensor-parallel-size / --data-parallel-size to hf endpoints deploy and update by @moon-bot-app[bot] in #4661

🤖 Inference

  • [Inference Providers] deepinfra: add text-to-speech support by @ovuruska in #4559
  • [Inference Providers] deepinfra: add feature-extraction support by @ovuruska in #4656

🖥️ CLI

🐛 Bug and typo fixes

  • [CLI] Fix scheduled upload of a single file in a subfolder by @dfedoryshchev in #4619
  • [Download] Fix tqdm_class ignored by the Xet transfer bar by @bharadwaj-pendyala in #4647
  • [CLI] Fix duplicated GPU rows in hf jobs stats by @dfedoryshchev in #4660
  • Don't report a 429 as a window rate limit when the window isn't exhausted by @moon-bot-app[bot] in #4662
  • [Download] Fix ResolvedRevision string value after pickle/copy by @hanouticelina in #4692
  • Do not use a redirect's Content-Length as file size in get_hf_file_metadata by @assafvayner in #4699
  • [Inference Endpoints] Omit model.task instead of sending null on create by @hanouticelina in #4701

📖 Documentation

🏗️ Internal

  • Post-release: bump version to 1.28.0.dev0 by @huggingface-hub-bot[bot] in #4643
  • Bump the actions group with 4 updates by @dependabot[bot] in #4652

[v1.27.0] Automatic `hf-cli` skill install, engine flags for Inference Endpoints & more

Choose a tag to compare

@github-actions github-actions released this 07 Aug 12:10

🤖 The hf-cli skill installs itself and stays in sync

The hf-cli skill teaches AI agents how to use the hf CLI, but until now you had to know it existed and install it by hand. The standalone installers (bash and PowerShell) now install it globally by default, pass --exclude-skill / -ExcludeSkill to skip — and hf update refreshes it afterwards, without ever bringing it back if you opted out or removed it. Any hf command also hints, at most once a day, when the skill is missing or was generated by another hf version. The hint is purely local, never installs anything on its own, and is silenced by HF_HUB_DISABLE_UPDATE_CHECK=1.

# The installer sets up the skill for you...
>>> curl -LsSf https://hf.co/cli/install.sh | bash -s
[INFO] Installing the hf-cli skill for AI agents...
Installed 'hf-cli' to central location: ~/.agents/skills/hf-cli
[INFO] Pass --exclude-skill to skip it.

# ...or skip it entirely
>>> curl -LsSf https://hf.co/cli/install.sh | bash -s -- --exclude-skill
[INFO] Skipping the hf-cli skill (--exclude-skill)
  • [CLI] Install & refresh the hf-cli skill (installer, update, hints) by @Wauplin in #4608

⚙️ Engine flags for Inference Endpoints, at deploy time and after

--container-command / --container-args no longer require --custom-image. That gate was conservative CLI scoping, not an API constraint: model.command and model.args are top-level fields of the endpoint payload and apply to managed engine images too, which is how the vLLM engine docs recommend passing engine flags. They can now also be changed after deploy — hf endpoints update gained both flags, and HfApi.update_inference_endpoint / InferenceEndpoint.update the matching container_command / container_args parameters. Values replace rather than append: pass an empty string to reset to the image default, or omit the flag to leave it untouched. --health-route and --port still require --custom-image, since they only exist on the custom image payload.

# Engine flags at deploy time, no custom image required anymore
>>> hf endpoints deploy my-endpoint --repo gpt2 --framework pytorch \
      --accelerator cpu --instance-size x2 --instance-type intel-icl \
      --region us-east-1 --vendor aws \
      --container-args "--max-model-len 8192"

# Change engine flags on an existing endpoint (previously UI / raw API only)
>>> hf endpoints update my-endpoint --container-args "--enable-auto-tool-choice --tool-call-parser lfm2"

# Reset to the image defaults
>>> hf endpoints update my-endpoint --container-args ""
  • [Inference Endpoints] Allow container command/args without custom image + support them in update by @gary149 in #4628

🚀 Baseten joins the inference providers

Baseten is now supported for the conversational task. It serves an OpenAI-compatible chat completions API, so there are no provider-specific quirks: target it with provider="baseten" and your own key, or let auto-routing pick it for any model already mapped on the Hub.

>>> from huggingface_hub import InferenceClient

>>> client = InferenceClient(provider="baseten", api_key="<BASETEN_API_KEY>")
>>> out = client.chat_completion(
...     model="zai-org/GLM-5.2",
...     messages=[{"role": "user", "content": "Hello!"}],
... )
>>> print(out.choices[0].message.content)
  • [Inference] Add Baseten as inference provider by @AlexKer in #3414

🔧 Other QoL Improvements

  • [HfApi] Add region to ExpandSpaceProperty_T by @hanouticelina in #4641 — the Hub added region as an expandable property for Spaces; it is now accepted by space_info / list_spaces and typed on SpaceInfo as Literal["us", "eu"] | Nonedocs
  • [Xet] Bump minimum hf-xet to 1.5.2 by @hanouticelina in #4640 — 1.5.2 fixes possible hangs on poor networks, but the floor was still 1.5.1, so fresh installs could land on the buggy version
  • Serialize model first in conversational payloads by @moon-bot-app[bot] in #4618 — routers can now resolve the provider from a small prefix instead of buffering a whole payload of base64 images. The resulting dicts are equal, only the key order changes

🐛 Bug and typo fixes

  • [HfFileSystem] Fix bucket prefix collisions by @lewtun in #4630 — the Buckets API applies prefix lexically, so in a bucket holding logs_existing/ but no logs/, exists(".../logs/new.txt") raised KeyError and ls(".../logs") could return the unrelated sibling. Listings are now filtered on path-component boundaries
  • [Cache] Stop deleting snapshot files twice when deleting a revision by @hanouticelina in #4639 — snapshot files that aren't symlinks into blobs/ (Windows copies, or files created by the user inside a snapshot dir) were deleted a second time as blobs, logging a FileNotFoundError traceback each. Reported freed size is unchanged, and per-path delete lines moved to debug
  • [Download] Don't retain caller frames when falling back to cache after a failed HEAD call by @Wauplin in #4614 — the swallowed HEAD exception kept its traceback, and with it the whole caller stack, alive until the next gc.collect(); vLLM had to monkey-patch this. Also fixes a v1.0 regression where http_backoff retried on an httpx client already closed by a previous ConnectError

🏗️ Internal

  • [Tests] Fix two Windows-only CI failures + flaky-CI findings by @Wauplin in #4615
  • Bump pinned doc-builder workflow SHA to 23dc84b by @mishig25 in #4617
  • Bump the actions group with 10 updates by @dependabot[bot] in #4636
  • Post-release: bump version to 1.27.0.dev0 by @huggingface-hub-bot[bot] in #4616

[v1.26.0] Resolve revisions only once, security hardening, and resource groups for Jobs & Collections

Choose a tag to compare

@github-actions github-actions released this 30 Jul 14:05

📌 Pin a revision once with resolve_revision

Libraries that download many files one by one (config, weights, tokenizer, processor, ...) had to resolve revision="main" into a commit hash on every call — costing one HTTP request per file and risking two calls landing on two different commits if the repo is updated in between. The new HfApi.resolve_revision resolves the revision once and returns a ResolvedRevision: a str subclass whose value stays the user-facing revision (so error messages keep saying "main") while its .resolved attribute holds the commit hash. Download helpers (hf_hub_download, snapshot_download, get_cached_repo_tree) detect it and use the commit hash directly, guaranteeing every file comes from the same commit. The mapping is also written to the refs/ folder of the cache, so later runs in offline mode transparently fall back to the cached value.

>>> from huggingface_hub import resolve_revision, hf_hub_download
>>> revision = resolve_revision("openai-community/gpt2")
>>> revision
ResolvedRevision(initial=None, resolved='607a30d783dfa663caf39e06633721c8d4cfcd7e')
>>> revision == "main"  # readable error messages
True
>>> config = hf_hub_download("openai-community/gpt2", "config.json", revision=revision)
>>> weights = hf_hub_download("openai-community/gpt2", "model.safetensors", revision=revision)

📚 Documentation: Manage the cache — Pin a revision (advanced)

  • [Download] Add HfApi.resolve_revision and ResolvedRevision by @Wauplin in #4604

🔒 Security hardening for downloads and sandboxes

This release ships two security fixes. First, downloading or uploading to a --local-dir now rejects absolute, drive-relative, root-relative, UNC and ..-traversal filenames on all platforms, interpreting each name under both POSIX and Windows rules (refs CVE-2026-15717). Previously only a Windows-only ..\ check existed, so a malicious repo could write files outside the target directory on Windows clients — and even leak a NetNTLMv2 hash via UNC paths. Legitimate repo filenames never contain such segments, so real downloads are unaffected; note that exotic names like folder/..\..\..\file, previously tolerated on Linux, are now rejected everywhere. Second, Sandbox.create no longer injects your HF token into the job environment to download the sbx-server binary: the bucket is public, so the bootstrap now downloads it anonymously and no HF credential ever lands in the sandbox unless you explicitly opt in with forward_hf_token=True.

  • [Download] Reject absolute/UNC/traversal filenames on local_dir & cache paths by @Wauplin in #4540
  • [Sandbox] Don't send the HF token to sandbox jobs by @Wauplin in #4583

🗂️ Resource groups for Jobs and Collections

Organization resource groups are now supported across the client. For collections, create_collection accepts an optional resource_group_id, and the new update_collection_resource_group method wraps the dedicated Hub endpoint to assign a collection to a resource group afterwards (passing None removes it). For Jobs, run_job, run_uv_job and create_scheduled_job accept a resource_group_id parameter, mirrored by a --resource-group-id option on the hf jobs run, hf jobs uv run and hf jobs scheduled run commands. Beyond access control within an organization, resource groups are also used for cost attribution and per-group spending limits.

hf jobs run --resource-group-id <group-id> python:3.12 python train.py

📚 Documentation: Collections reference, CLI reference

  • Support resource groups for collections by @moon-bot-app[bot] in #4575
  • feat(jobs): support resource group at Job creation by @moon-bot-app[bot] in #4576

📊 Job names, front and center in the CLI

Job names are now much easier to work with from the terminal. hf jobs ls (and hf jobs scheduled ls) display a dedicated NAME column, and a new --name filter acts as a shortcut for --label name=NAME. The name is also surfaced as a top-level field in hf jobs inspect and in command results, instead of only living inside labels — where it remains for compatibility.

$ hf jobs ls -a --name training-v2
JOB_ID      NAME         IMAGE/SPACE COMMAND      CREATED      STATUS    RUNTIME
----------- ------------ ----------- ------------ ------------ --------- -------
6a60b190... training-v2  python:3.12 python -c... 2026-07-2... COMPLETED 0s

📚 Documentation: Run and manage Jobs

  • [Jobs] Surface Job name as a first-class field in the CLI by @Wauplin in #4568

📖 Documentation

  • Added Odia (or) translation of the index, installation and quick-start pages by @indrajeetapache in #4454
  • [Docs] Fix Odia (or) docs build, register it in CI, rename tm -> ta by @Wauplin in #4589 — note: Tamil docs URLs move from /tm/ to /ta/ (correct ISO 639-1 code)
  • [Docs] Fix HF_XET_SHARD_CACHE_SIZE_LIMIT default (4GB → 16GB) by @rajatarya in #4593docs
  • docs(jobs): mention cost attribution/spending-limit in resource_group_id docs by @Pierrci in #4597docs

🐛 Bug and typo fixes

  • [Download] Reject redacted Xet hashes from tree cache by @seanses in #4595 — fixes xet downloads failing with Unable to parse string as hex hash value on gated repos without content access
  • [Safetensors] Fix truncated header on 100kb boundary by @Wauplin in #4603 — headers of 99994–100000 bytes were silently truncated and failed with header is not json-encoded string
  • Reject token=False in create_inference_endpoint_from_catalog instead of silently ignoring it by @ckarnell in #4605
  • [CLI] Don't crash when stdout can't encode non-ASCII output by @Wauplin in #4610 — fixes UnicodeEncodeError on Windows when output is redirected or piped
  • [Core] Fix tilde expansion in CommitOperationAdd by @Saniyagupte in #4612 — paths like ~/model.bin no longer raise FileNotFoundError on upload

🏗️ Internal

  • [Bot] Update hardware flavor enums and docs by @huggingface-hub-bot[bot] in #4577
  • Post-release: bump version to 1.26.0.dev0 by @huggingface-hub-bot[bot] in #4584
  • [CI] [Release] Make notify-prs best-effort with a retry by @Wauplin in #4585
  • [Release] [CI] Update slack message generation by @Wauplin in #4587
  • [Release] [CI] Fix slack message generation follow-ups by @Wauplin in #4588
  • [CI] Unpin pytest-rerunfailures to fix cascading fixture setup errors by @Wauplin in #4590

[v1.25.0] Auto-named Jobs, smarter progress bars & cache diagnostics

Choose a tag to compare

@github-actions github-actions released this 27 Jul 08:49

🏷️ Auto-named Jobs on creation

Jobs now get an automatic name when you don't provide one explicitly, derived from the Docker image (or UV script) plus a short hash of the command line. This means reruns of the same command share a consistent name, while different commands get distinct names — making it much easier to find and group related jobs in the UI or CLI. Names follow the server-side character rules: :, / and . in image tags are replaced with - so python:3.12 foo --truc becomes python-3-12-7c6db949. Explicit --name still takes precedence.

>>> hf jobs run --detach python:3.12 foo --truc
  id: 6a60b85c13e6ef894d54b949
Hint: Job auto-named 'python-3-12-7c6db949'. Pass `--name` or run `hf jobs labels <id> --name` to rename.

📚 Documentation: Jobs guide, CLI guide

🔧 Other QoL Improvements

  • Add timeout parameter to safetensors metadata methods by @go-bai in #4378
  • [Download] [Fix] Update file-count progress bar on completion by @Wauplin in #4560
  • [Cache] Warn on inconsistency in cache by @Wauplin in #4551docs

📖 Documentation

🐛 Bug and typo fixes

  • [Repocard] Fix catastrophic backtracking (ReDoS) in REGEX_YAML_BLOCK by @sohumt123 in #4526
  • Fix Windows crash when downloading into a deep local_dir by @askalf in #4546
  • [Fix] Do not fail on create space if exists_ok=True and 402 Payment required error by @Wauplin in #4539
  • [Repocard] Preserve order of appended keys in CardData.to_yaml by @rahulrshetty45 in #4561
  • [HfFileSystem] Raise FileNotFoundError when streaming a missing file by @rahulrshetty45 in #4562
  • [Upload] Forward token in _final_commit_info repo_info lookup by @Wauplin in #4572

🏗️ Internal

  • Post-release: bump version to 1.25.0.dev0 by @huggingface-hub-bot[bot] in #4538
  • [CI] Remove test_list_private_datasets flaky test by @Wauplin in #4565

[v1.24.0] Name your Jobs! (and download fixes)

Choose a tag to compare

@github-actions github-actions released this 17 Jul 09:47

📊 Name your Jobs!

Jobs on the Hub now support an optional --name flag on the CLI and a name parameter on the Python API (run_job, run_uv_job, create_scheduled_job, create_scheduled_uv_job). Names are stored as the name label and make Jobs easier to find and identify in the UI. You can also name an existing Job using hf jobs labels <job_id> --name my-job. Names are optional and do not need to be unique.

# Create a named Job
hf jobs run --name training-v2 python:3.12 python train.py

# Name an existing Job
hf jobs labels <job_id> --name training-v2

# Named scheduled Job
hf jobs scheduled run @hourly --name hourly-task python:3.12 python -c 'print("This runs every hour!")'

📚 Documentation: CLI guide, Jobs guide

  • [Jobs] Add optional names to Jobs CLI and API by @Wauplin in #4532

📖 Documentation

  • [Docs] Fix HTTP client name in download docs by @cupkk in #4523

The README has been completely refreshed to put the hf CLI first. The standalone installer (curl/PowerShell) and a terminal quick start — covering auth login, models ls, download, upload, and jobs run — now appear before the Python library section. A new For AI agents section introduces hf skills add for Codex, Cursor, OpenCode, Claude Code, and other AI tools. The Python content remains intact under the renamed Use the Python library heading, with refreshed example models and a corrected tagline ("The official CLI and Python client for the Hugging Face Hub").

🐛 Bug and typo fixes

  • [Download] Fix xet download rate: show summed, not per-file speed by @rajatarya in #4530

🏗️ Internal

  • Bump the actions group with 4 updates by @dependabot[bot] in #4503
  • Post-release: bump version to 1.24.0.dev0 by @huggingface-hub-bot[bot] in #4516

[v1.23.0] Space templates, CLI extension updates & smoother Xet downloads

Choose a tag to compare

@github-actions github-actions released this 09 Jul 13:50

🚀 Create Spaces from templates

You can now seed a new Space from one of the official Hub templates (JupyterLab, a Gradio chatbot, a Streamlit app, etc.) instead of starting from an empty repo. List what's available with the new list_space_templates() API or the hf spaces templates CLI command, then pass a template's repo_id (or its short name) to create_repo(..., space_template=...) or hf repos create --type space --template. The Space SDK is inferred from the template, and templates recommended as private (like JupyterLab) are created privately by default unless you explicitly choose a visibility.

# List available templates
$ hf spaces templates
NAME        REPO_ID                             SDK     PREFERRED_PRIVATE
----------- ----------------------------------- ------- -----------------
Streamlit   streamlit/streamlit-template-space  docker
JupyterLab  SpacesExamples/jupyterlab           docker  ✔

# Create a Space from a template
$ hf repos create my-jupyterlab --type space --template jupyterlab
✓ Repo created
  repo_id: Wauplin/my-jupyterlab
  url: https://huggingface.co/spaces/Wauplin/my-jupyterlab
>>> from huggingface_hub import create_repo
>>> create_repo("my-jupyterlab", repo_type="space", space_template="jupyterlab")
  • Support creating a Space from a template by @moon-bot-app[bot] in #4504

🔌 Update installed CLI extensions

A new hf extensions update command brings your installed CLI extensions to their latest published version on GitHub. Pass a name to update a single extension, or run it with no argument to check every installed extension and update the ones that are behind. Updates are applied in place — Python extensions reuse their existing venv and binary extensions are overwritten — so a failed update no longer leaves the extension uninstalled, and extensions that are already up to date are simply skipped.

# Update a single extension (accepts <name>, hf-<name> or OWNER/hf-<name>)
hf extensions update hf-claude

# Check every installed extension and update the outdated ones
hf extensions update

📶 Smoother Xet download progress with dual bars

Xet downloads now show two progress bars so you can tell a transfer is alive even on a slow connection. The transfer bar advances as bytes arrive over the network, while the reconstruction bar tracks real progress as buffered chunks are written to disk — previously the single bar could sit at 0% for a long time while data was actually arriving. The dual bars are wired into single-file downloads (hf_hub_download), snapshot_download (where parallel file downloads feed the repo-level transfer and reconstruction bars), the hf download CLI, and bucket downloads.

big.bin: downloading bytes:   |  52.4MB     1.2MB/s
big.bin: reconstructing file: |  52.4MB / 105MB     800kB/s
  • Smoother Xet download progress with dual bars by @seanses in #4400

🤖 Always up-to-date, offline hf-cli skill

hf skills add and hf skills update now generate the built-in hf-cli skill locally from your installed CLI version instead of downloading it from the marketplace bucket. The installed SKILL.md is therefore always in sync with the CLI you're running, and installing or updating the hf-cli skill works fully offline — the marketplace is only contacted when you install another managed skill. As defense-in-depth against path traversal, skill names coming from the marketplace payload are now validated before any filesystem work.

# Works fully offline, and always matches your installed CLI version
$ HF_HUB_OFFLINE=1 hf skills add --dest ./skills
Installed 'hf-cli' to ./skills/hf-cli
  • [CLI] Generate hf-cli skill locally instead of downloading from bucket by @hanouticelina in #4199

🖥️ CLI

  • [CLI] Filter hf models ls by inference provider by @moon-bot-app[bot] in #4497docs
  • [CLI] Add --pipeline-tag, --gated, --apps filters to hf models ls by @Wauplin in #4512docs
  • [CLI] --sdk instead of --space-sdk in CLI for consistency by @Wauplin in #4505docs
  • Correct hf jobs/spaces --timeout help: durations are int, not int/float by @Sreekant13 in #4477

🔧 Other QoL Improvements

  • [Sandbox] Optimize parallel file transfer constants for better throughput by @Wauplin in #4490
  • Expose snapshot_path on IncompleteSnapshotError by @moon-bot-app[bot] in #4500docs
  • [Utils] Add get_cached_repo_tree utility by @Wauplin in #4513docs

📖 Documentation

  • [i18n-HI] Add Hindi translation for guides/search by @Yash4616 in #4428
  • [i18n-HI] Add Hindi translation for download guide by @Mr-Abhinav-Pandey in #4450
  • [i18n-HI] Add Hindi translation for guides/upload by @Yash4616 in #4463
  • [i18n-HI] Add Hindi translation for guides/repository by @Yash4616 in #4464
  • docs: drop /new from Inference Endpoints web interface links by @moon-bot-app[bot] in #4509

🐛 Bug and typo fixes

  • [Utils] Treat backslashes as path separators in filter_repo_objects by @Wauplin in #4506 — fixes a v1.22 regression where snapshot_download silently skipped files on Windows
  • [CLI] Coerce enum member defaults to their value when building click params by @dhruv7477 in #4494 — fixes a v1.22 regression where commands using an enum option default failed at runtime
  • Update install.ps1 by @ufocia in #4501 — fixes false install verification failures on Windows

🏗️ Internal

  • [Xet] Remove dead connection info helpers by @Wauplin in #4482
  • [CI] Fix Transformers RC testing by @Wauplin in #4481
  • [CI] Rename transformers-test-ci to transformers-ci by @ydshieh in #4495
  • [CI] Fix expand property type tests by @Wauplin in #4510
  • Make doc builds faster by @mishig25 in #4489
  • Keep doc-builder pin comment dependabot-compatible by @mishig25 in #4491
  • Post-release: bump version to 1.23.0.dev0 by @huggingface-hub-bot[bot] in #4480

[v1.22.0] Sandboxes, faster downloads, and a rebuilt CLI

Choose a tag to compare

@github-actions github-actions released this 03 Jul 09:43

🖥️ 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 sandbox CLI 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.

  • [Download] Cache repo tree listing on disk in snapshot_download by @Wauplin in #4394

📚 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.

💔 Breaking Change

  • [Upload] Deprecate upload_large_folder (API + CLI) by @Wauplin in #4414upload_large_folder and hf upload-large-folder are now deprecated in favor of upload_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 #4435allow_patterns/ignore_patterns now match case-sensitively on every OS (aligned with case-sensitive Hub paths). On Windows this is a behavior change: patterns like *.PDF no longer match file.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 edit by @Wauplin in #4415docs
  • [CLI] hf cache: surface & prune incomplete downloads by @Wauplin in #4416hf cache ls now flags leftover .incomplete files and hf cache prune removes them automatically — docs
  • [CLI] Point users at hf jobs wait in job hints by @davanstrien in #4429
  • [CLI] Expose out singleton publicly + add out.log method by @Wauplin in #4471

🤖 Inference

  • [Inference Providers] deepinfra: add automatic-speech-recognition support by @ovuruska in #4382

📊 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-artifacts bucket and mount it; -v accepts local directories in hf jobs run/uv run (and scheduled variants) — docs
  • [Jobs] Add hf jobs scheduled trigger ... to trigger scheduled jobs on demand by @Wauplin in #4459docs

🔧 Other QoL Improvements

  • [Http] Support standard Retry-After header in http_backoff by @Wauplin in #4460http_backoff now honors the standard Retry-After header (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=False to get_dataset_leaderboard to include fine-tuned/derivative repos that declare a parent model.

📖 Documentation

🐛 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 --filter thresholds like size>1GB now 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

[v1.21.0] Jobs filtering & pagination

Choose a tag to compare

@github-actions github-actions released this 25 Jun 12:48

📊 Jobs listing revamped: filter, paginate, and ls instead of ps

The Jobs listing API and CLI have been overhauled with server-side filtering, proper pagination, and a CLI rename that aligns with the rest of hf. list_jobs() now accepts status and labels parameters that push filtering to the server, and returns a lazy iterator (matching list_models, list_datasets, etc.) so large result sets are fetched page by page. On the CLI side, hf jobs ps has been renamed to hf jobs ls for consistency with hf repos ls, hf models ls, and friends — ps and list still work as aliases.

⚠️ Breaking changes:

  • list_jobs() now returns an Iterable[JobInfo] instead of list[JobInfo]. If you indexed the result (jobs[0]), wrap it with list(...).
  • -f/--filter in hf jobs ls is deprecated. Use --status and --label instead. Glob patterns (data-*), negation (key!=value), and filtering by id/image/command are no longer supported.
from huggingface_hub import list_jobs

# Filter by status and labels
list_jobs(status=["RUNNING", "SCHEDULING"], labels={"env": "prod"})

# Iterate lazily
for job in list_jobs():
    print(job.id)

# Materialize all results
all_jobs = list(list_jobs())
# Filter by status and labels
hf jobs ls --status running,scheduling --label env=prod --label team=ml

# Paginate with --limit
hf jobs ls -a --limit 500
hf jobs ls -a --limit 0  # no limit
  • [Jobs] Add stage/label filtering to list_jobs API and CLI by @Wauplin in #4395
  • [Jobs] Paginate list_jobs and add --limit to hf jobs ps by @Wauplin in #4403
  • [CLI] [Jobs] Rename job listing to 'hf jobs ls' (and keep 'hf jobs ps' alias) by @Wauplin in #4409

📚 Documentation: CLI guide, Jobs guide

🐛 Fix circular import on from huggingface_hub import login

A regression introduced in v1.20.0 caused from huggingface_hub import login to raise an ImportError on a fresh interpreter, due to a circular dependency between _oauth_device and utils._http. The fix moves _oauth_device.py into the utils layer so all imports resolve downward, eliminating the cycle. No lazy imports or workarounds required.

🔧 Other QoL Improvements

📖 Documentation

🐛 Bug and typo fixes

  • [Serialization] Exclude skipped string tensors from single-shard index by @nyxst4ck in #4391
  • Fix IndexError in filter_repo_objects on an empty allow/ignore pattern by @CharlesCNorton in #4402

🏗️ Internal

  • Post-release: bump version to 1.21.0.dev0 by @huggingface-hub-bot[bot] in #4383
  • [CI] Catch circular imports in the check-imports job by @hanouticelina in #4386
  • [Tests] Fix kernel tests for moved file on production by @Wauplin in #4387
  • [Release] Report trigger actor, drop social drafts, archive raw + edited notes by @Wauplin in #4388
  • [Tests] Fix flaky security-scan assertions by @Wauplin in #4392
  • [Bot] Update hardware flavor enums and docs by @huggingface-hub-bot[bot] in #4406
  • [CLI] Fix ty/mypy errors with click 8.4.2 by @Wauplin in #4408

v1.20.1

Choose a tag to compare

@github-actions github-actions released this 25 Jun 13:09

Full Changelog: v1.20.0...v1.20.1

[v1.20.0] Browser-based OAuth login, multi-commit folder uploads, and more

Choose a tag to compare

@github-actions github-actions released this 18 Jun 12:20

🔒 Browser-based OAuth login

hf auth login now defaults to a browser-based OAuth Device Code flow instead of asking you to copy-paste a token. The command prints a URL and a short code, you authorize in the browser, and the CLI retrieves and saves the token for you. The same applies to login() in Python. In an interactive terminal you still get a gh-style arrow-key menu to pick between browser login and pasting a token, and --token works exactly as before.

OAuth tokens expire after 30 days, but they come with a refresh token: get_token() transparently refreshes them when less than a day of validity remains, so long-running setups keep working without re-authenticating. hf auth list now shows the expiry date for OAuth tokens.

> hf auth login
? How would you like to log in? Log in with your browser

    Open this URL in your browser:
        https://hf.co/oauth/device

    And enter the code: 52AT-FLYZ

    Waiting for authorization.

When the command is run by an AI agent, it never prompts. Instead it streams structured events so the agent can surface the URL and code to its user, then blocks until a terminal auth_success / auth_error event:

$ hf auth login --format json
{"event": "device_code", "verification_uri": "https://hf.co/oauth/device", "user_code": "52AT-FLYZ", "verification_uri_complete": "https://hf.co/oauth/device", "expires_in": 300, "interval": 5}
{"event": "auth_success", "user": "celinah", "token_name": "oauth-celinah"}

hf auth list surfaces the new expiry column:

$ hf auth list
  name          token       expires
- ------------- ----------- -------------------
  my-token      hf_****5678
* oauth-user    hf_****1234 2026-07-09
  oauth-old     hf_****9999 2026-06-09 (expired)

Finally, notebook_login() now renders the link and code with plain IPython.display.HTML, dropping the ipywidgets dependency.

⚡ Faster, more reliable hf upload for large folders

hf upload and the underlying upload_folder have been revamped to be faster and far more robust on large folders. When hf_xet is installed (the default), uploads now run through a streamed, multi-commit pipeline built on the XetSession API: the folder is scanned and fed into a background Xet upload while previous batches are committed in parallel, and files are hashed in a single read pass while they are chunked (the old flow read every large file twice). Nothing changes in how you call it:

hf upload <repo-id> <path/to/folder>

This is a drop-in replacement for experimental hf upload-large-folder used until today, which will be deprecated in a future release.

🚨🚨 Breaking change: With the upload_folder and hf upload revamp, uploading a folder might result in multiple commits. It is also not possible to open a PR against a specific revision while using upload_folder. If you pass create_pr=True, it will necessarily create a PR against main. It will open the PR no matter if some changes have been committed (previously an empty commit was resulting in no PR opened at all).

What you get on large folders:

  • More reliable. Uploads are resumable and stateless. If an upload is interrupted, just re-run the same command: already-committed files are detected and skipped, and already-uploaded chunks are deduplicated by the Xet backend (≈0 bytes re-transferred). There are no local state files to go stale, so resume even works from a different machine.
  • Faster. Files are hashed while being chunked (single read pass) and batches commit in the background while the next batch is already uploading, so there is no separate hashing phase blocking the upload.
  • Multi-commit by default. Large folders are automatically split into adaptive commits that scale between 64 and 1024 files based on commit duration. Folders that fit in a single batch still produce exactly one commit, as before; follow-up commits get a (part N) suffix.
  • Live progress bar tracking the preparing, uploading, and committing stages (with a plain-log fallback when output is not a TTY):
Found 301 files to upload
  Preparing   ████████████████████  301 / 301 ✓
  Uploading   █████████████████░░░  255 / 300 files  25.5MB · 1.86MB/s
  Committing  ░░░░░░░░░░░░░░░░░░░░  0 / 301

upload_large_folder / hf upload-large-folder are intentionally left untouched in this release; their deprecation will follow once hf upload has fully absorbed the use case.

  • [Upload] Streamed multi-commit upload_folder powered by Xet by @Wauplin in #4331

💻 Jobs: wait, SSH access, and cleaner error messages

This release adds three major capabilities to Hugging Face Jobs.

Wait for completion. HfApi.wait_for_job() and hf jobs wait block until one or more Jobs reach a terminal stage, which makes it easy to chain commands in CI scripts. wait_for_job accepts a single id or a list, returns the final JobInfo even on failure (check job.status.stage), and only raises TimeoutError on timeout. The CLI exits 0 only if all waited-on Jobs ended COMPLETED.

# Wait on a single job, then run the next step only if it succeeded
hf jobs wait <job_id> && next-step

# Wait on a batch, with a timeout
hf jobs wait <id1> <id2> --timeout 10m

⚠️ Breaking change: non-detached hf jobs run / hf jobs uv run now exit with the Job's outcome (exit code 1 if the Job errored) instead of always exiting 0. We consider this a bugfix — scripts relying on the old behavior were being silently misled — but it is called out here in case you depend on the previous exit code.

SSH access. With --ssh at launch and an SSH key registered on huggingface.co/settings/keys, you can connect straight into a running Job's container with hf jobs ssh <job_id>. Thanks to wait_for_job, hf jobs ssh now waits for the Job to reach RUNNING before connecting (with a status spinner) instead of failing immediately while it is still scheduling.

$ hf jobs run --ssh --detach python:3.12 sleep infinity
✓ Job started
  id: 6a33ba2aef9220ea67d98a03
  url: https://huggingface.co/jobs/Wauplin/6a33ba2aef9220ea67d98a03
Hint: Use `hf jobs ssh Wauplin/6a33ba2aef9220ea67d98a03` to open an SSH session into the job.

$ hf jobs ssh Wauplin/6a33ba2aef9220ea67d98a03
Job is running.
Running `ssh 6a33ba2aef9220ea67d98a03@ssh.hf.jobs`
root@j-wauplin-6a33ba2aef9220ea67d98a03-do4bduvn-5f153-458k4:/#

Readable errors. A new JobNotFoundError and the switch from response.raise_for_status to hf_raise_for_status turn raw httpx tracebacks into clean, actionable messages. Per-command try/except blocks were removed in favor of the global CLI error handling.

$ hf jobs inspect 000
Error: 404 Client Error. (Request ID: Root=1-6a316470-...)

Job Not Found for url: https://huggingface.co/api/jobs/Wauplin/000.
Please make sure you specified the correct job ID and namespace.
Set HF_DEBUG=1 as environment variable for full traceback.
  • [Jobs] Add hf jobs wait and HfApi.wait_for_job by @Wauplin in #4345
  • [Jobs] Add SSH support to run a Job and connect to it by @Wauplin in #4352
  • [CLI] Make hf jobs ssh wait for job to be running by @Wauplin in #4379
  • Add new JobNotFound error by @Wauplin in #4367

🖥️ Custom-container deploy for Inference Endpoints

hf endpoints deploy can now deploy custom Docker containers end-to-end, no more hand-writing JSON and POSTing the raw endpoints API. New flags wire up the image and its runtime: --custom-image, --health-route, --port, --command, and --container-args. Environment variables and secrets can be injected with --env/--env-file and --secrets/--secrets-file. On the SDK side, create_inference_endpoint gains container_command and container_args parameters.

hf endpoints deploy nex-n2-pro \
  --repo nex-agi/Nex-N2-Pro \
  --framework custom \
  --accelerator gpu --vendor aws --region us-east-1 \
  --instance-type nvidia-h200 --instance-size x8 \
  --custom-image nexagi/sglang:v0.5.12 \
  --health-route /health --port 30000 \
  --container-args "--reasoning-parser qwen3 --tool-call-parser qwen3_coder --mamba-scheduler-strategy extra_buffer --tp 8" \
  --env MODEL_ID=/repository \
  --type authenticated

The type parameter now defaults to authenticated instead of the deprecated protected (passing protected emits a FutureWarning). The custom-container flags raise a clean error if used without --custom-image.

  • [Inference Endpoints] Custom-container deploy CLI + deprecate protected endpoint type by @gary149 in #4329

⏳ Wait for a Space with wait_for_space and hf spaces wait

Mirroring the new wait_for_job primitive, HfApi.wait_for_space() and hf spaces wait block until a Space leaves an intermediate stage (BUILDING, APP_STARTING, …) and settles on a final state. The CLI exits 0 if the Space is RUNNING, non-zero otherwise. hf spaces ssh and hf spaces dev-mode were refactored to use wait_for_space internally instead of the old CLI-only helper.

# Wait after a restart
hf spaces restart username/my-space && hf spaces wait username/my-space

# With a timeout
hf spaces wait username/my-space --timeout 5m
>>> from huggingface_hub import restart_space, wait_for_space
>>> restart_space("username/my-space")
>>> runtime = wait_for_space("username/my-space")
>>> runtime.stage
'RUNNING'

📚 Documentation: CLI guide — wait for a Space · Space runtime reference

  • [Spaces] Add wait_for_space API and `...
Read more