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

bash
soup train --push-as alpamys/chat-llama

Every 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

bash
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

bash
soup push --model ./output \
  --repo alpamys/chat-llama \
  --collection alpamys/kazakh-llms-abc123

Adds 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)

bash
HF_ENDPOINT=https://hf.internal.example.com soup train --push-as team/my-model

Every 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.0 explicitly 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/10 gets the message that says to use HTTPS; since v0.75.2 that message also covers 100.64.0.0/10). Over HTTPS the address is not checked: an https:// 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

bash
soup data push --input data.jsonl --hf-dataset alpamys/kazakh-chat

Uploads 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

bash
soup deploy hf-space \
  --model alpamys/chat-llama \
  --space alpamys/chat-llama-demo \
  --template gradio-chat   # or: streamlit-chat

Creates 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_lineage parameter
  • 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:

  1. Env: HF_TOKEN or HUGGINGFACE_HUB_TOKEN
  2. ~/.cache/huggingface/token
  3. ~/.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: true training 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

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.