HuggingFace Hub Deep Integration
Soup v0.29.0 closes the loop between training and the Hub. Every checkpoint can auto-push as a branch, training can resume from the Hub after a crash, models deploy to a Gradio or Streamlit Space in one command, and self-hosted Hubs are supported via HF_ENDPOINT.
Auto-push every checkpoint
soup train --push-as alpamys/chat-llamaEvery save_steps checkpoint is uploaded to HuggingFace Hub as a checkpoint-<N> branch. The main branch is updated only at training completion. A sticky _repo_failed flag means push failures (bad token, rate limit) short-circuit subsequent saves — no log spam, no training crash.
The upload uses an explicit allow_patterns allowlist (*.safetensors, *.bin, *.pt, *.json, tokenizer*, trainer_state.json, training_args.bin, README.md) — an output_dir that overlaps with your project root cannot accidentally publish .env or source files.
Resume from the Hub
soup train --push-as alpamys/chat-llama --hf-resume--hf-resume pulls the latest checkpoint-<N> branch via snapshot_download, places it in output_dir (containment-checked against cwd), and resumes. local_dir_use_symlinks=False defeats symlink-based filesystem escapes on older huggingface_hub versions.
Use this when a spot GPU dies, a preemption kicks you off, or you want to migrate training between machines.
HF Collections
soup push --model ./output \
--repo alpamys/chat-llama \
--collection alpamys/kazakh-llms-abc123Adds the pushed repo to an existing HuggingFace Collection. The slug follows the owner/slug-hash regex; null bytes, whitespace, and .. are rejected. Max 256 chars.
Self-hosted Hub (HF_ENDPOINT)
HF_ENDPOINT=https://hf.internal.example.com soup train --push-as team/my-modelEvery HF operation routes to your internal Hub. SSRF hardening:
- Scheme allowlist (http/https only)
- Plain HTTP permitted only for loopback (
localhost/127.0.0.1/::1) 0.0.0.0explicitly rejected- Plain HTTP to any non-loopback host is rejected (a private, link-local or cloud-metadata address such as RFC1918, 169.254.x or
100.64.0.0/10gets the message that says to use HTTPS; since v0.75.2 that message also covers100.64.0.0/10). Over HTTPS the address is not checked: anhttps://endpoint naming a private IP is accepted, and the private-address refusal added in v0.75.2 does not cover this variable. Hostnames are never resolved - Null bytes rejected
Publish datasets
soup data push --input data.jsonl --hf-dataset alpamys/kazakh-chatUploads a local JSONL as a HuggingFace dataset repo. Input path must stay under cwd, repo_id is validated, and a token is resolved via the unified utils/hf.resolve_token chain. Commit messages are stripped to the first line and capped at 200 chars to prevent multi-line injection into public HF commit history.
Deploy to HF Spaces
soup deploy hf-space \
--model alpamys/chat-llama \
--space alpamys/chat-llama-demo \
--template gradio-chat # or: streamlit-chatCreates a Space wrapping your fine-tuned model. render_space_template validates model_repo via validate_repo_id before substituting into app.py / README.md — a crafted repo id cannot inject Python code into the deployed Space.
To use your own template, pass --template-dir <dir> instead of --template. The directory must contain app.py and README.md and may contain requirements.txt; every {MODEL_REPO} in those files is replaced with the validated --model value. The directory has to sit under the working directory, a template file that is a symlink is refused, and each file is capped at 256 KB. The Space SDK is picked from the requirements.txt: the first line naming streamlit or gradio decides it, and a template with neither, or with no requirements.txt, is a Gradio Space. There is no --sdk flag. --private creates the Space as private and --yes skips the confirmation prompt. The command needs a Hugging Face token (HF_TOKEN or a cached login) and exits 1 without one.
Model card v2
Generated READMEs now include:
- Training config: task, base model, learning rate, optimizer
- Optional eval scorecard from an
eval_results.json - HTML-escaped
data_lineageparameter - Markdown-active chars neutralized in task names and non-numeric score values (
|,[,],(,),!,<,>, newlines, tabs) — no table-row / link / image / raw-HTML injection in the rendered README
Token resolution
utils/hf.resolve_token is the single source of truth:
- Env:
HF_TOKENorHUGGINGFACE_HUB_TOKEN ~/.cache/huggingface/token~/.huggingface/token
Non-printable tokens are rejected. The legacy soup push --token flag now warns deprecated and delegates to this chain.
repo_id validation
Applied to soup push --repo, soup train --push-as, soup data push --hf-dataset, soup deploy hf-space --model/--space:
- Alphanumeric +
._-only - Per-component ≤ 96 chars
- Total ≤ 200 chars
- Null-byte / whitespace /
../ leading-/rejection
Which downloads get the namespace pin
Soup keeps a local trust-on-first-use pin of a Hugging Face model repo's author and creation time (see Supply chain security). The check runs inside Soup's own hub download wrapper, utils/hubs.snapshot_download, and only four paths reach it at v0.76.0:
soup spectrum scan <hf-id>soup probe sae-diff --auto-download- the base model of a
stream_layers: truetraining run - a Hub id given as
training.prm_reward(org/name, optionally@revision), which v0.76.0 now downloads before training starts instead of failing at the first reward call (#1466); any other value that is not an existing directory under the working directory is refused
Everything else on this page does not go through it. --hf-resume calls huggingface_hub.snapshot_download directly, and an ordinary from_pretrained load (soup train without streaming, chat, serve, merge, export) is not pinned either. The pin fails open when the Hub metadata is missing or the pin store cannot be read.
License migration
As of v0.29.0, Soup is Apache-2.0 (previously MIT). Downstream redistributors must retain the NOTICE file per §4(d).
See also
- Registry — track every pushed run locally
- Eval-gated training — catch regressions before pushing to the Hub
- CLI reference
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.