CLI Reference

This page covers the v0.76.0 binary: 78 top-level commands, 29 of them groups with subcommands, and the global flags at the end. Six commands are new in v0.76.0 (soup rewind, soup bench train, soup data clean, soup recipes verify, soup adapters check and soup adapters audit), and soup bench became a group whose infer subcommand is the old inference benchmark; they and every other v0.76.0 change are collected under v0.76.0: new commands and flags, and Every top-level command is an index with the page that documents each one.

Exit codes are defined per command, not uniformly across Soup. Since v0.76.0 eleven gate and verdict commands share one taxonomy: 0 pass, 2 gate failure, regression or drift, 3 usage or input error, and a mistyped flag on one of them is 3 as well. Every other command keeps its own scheme (a few individual codes moved too, see what moved), and a mistyped flag on those is still 2. A CI step that tests for exit 1 as "the gate failed" now lets a failing gate through (what moved). Exit codes lists what each command returns.

Core Commands

soup init

Create a config file with interactive wizard or template.

bash
soup init [--template <template>] [--output <path>]

soup train

Start a training run.

bash
soup train [--config <path>] [--resume auto|<checkpoint>] [--wandb] [--tensorboard] \
           [--deepspeed zero2|zero3|zero2_offload|zero3_offload|zero++|zero_pp] [--fsdp full_shard|shard_grad|full_offload] \
           [--gpus auto|<N>] [--nodes <N> --node-rank <R> --master-addr <host> [--master-port <port>]] \
           [--no-reexec] [--gate <suite>] \
           [--push-as <user/repo>] [--hf-resume] [--yes]
  • --gpus (v0.27) — GPUs per node for distributed training (auto or an integer): Soup detects NVLink vs PCIe and re-launches under accelerate launch. Since v0.76.0 it exits with the launcher's exit code on every platform; on Windows it now waits for the launcher (through v0.75.2 the call returned 0 at once). --no-reexec prints the launch command instead of launching it and exits 1 (it does not exit 1 when --dry-run is also given).
  • --nodes, --node-rank, --master-addr, --master-port (v0.76.0) — multi-node Accelerate launch. --nodes above 1 requires --gpus (GPUs per node, the same on every node) and --master-addr; --master-port defaults to 29500. A launch-setting mistake exits 1 with Invalid distributed launch: .... See multi-node launch.
  • --deepspeed zero++ (v0.27) — ZeRO-3 with quantized weights & grads
  • --gate (v0.26) — run an eval suite at epoch boundaries and halt on regression
  • --push-as (v0.29) — auto-push every save_steps checkpoint to HF Hub as a checkpoint-<N> branch
  • --hf-resume (v0.29) — pull the latest checkpoint branch from HF Hub and resume training
  • --cloud modal|lambda with --gpu <type> and --cloud-submit — plan-only unless you add --cloud-submit; see serverless cloud training. --cloud runpod is recognised and refused (RunPod cloud training is not yet live; use --cloud modal or lambda., exit 2), and any other value exits 2 with Invalid --cloud.

soup chat

Interactive chat with a trained model.

bash
soup chat --model <path> [--base <model>] [--temperature <float>] [--max-tokens <int>]

soup serve

Start an OpenAI-compatible inference server.

bash
soup serve --model <path> [--backend transformers|vllm|sglang|mii] [--port <int>] [--host <host>] [--tensor-parallel <n>] [--gpu-memory <float>] [--speculative-decoding <draft-model>] [--num-speculative-tokens <n>] [--adapters <name>=<path>]

--adapters (several named LoRA adapters on one server) works with --backend transformers only; with vllm, sglang or mii it exits 1. To serve one adapter on vLLM, pass the adapter directory itself as --model: the base model is read from its adapter_config.json.

--auto-quant exits 2 with --auto-quant is unavailable since v0.76.0: Soup cannot measure quantization candidates before an engine has loaded them, so it refuses instead of guessing a format. Any --host other than loopback needs --tool-auth-token, which gates the tool, thumbs and adapter routes only; the generation routes take no token on any backend.

soup export

Export model to deployment format.

bash
soup export --model <path> --format gguf|gguf-ud|onnx|tensorrt|awq|gptq|bitnet|tq1_0|torchao [--quant <type>] [--output <path>]
soup export --format awq|gptq --calibration-data <p> [--bits 4|8] [--group-size 128]
# awq and gptq are DEPRECATED in v0.76.0 and the notice says they will be removed in Soup v0.77: both start
# with a "Deprecated:" notice that names gguf, onnx, tensorrt, bitnet and tq1_0 instead.
# --calibration-data is REQUIRED for both awq and gptq, and --calibration-samples caps
# the rows read (default 128). Refused before the quantizer is imported: gptq since
# v0.74.0, awq since v0.75.0, where a flagless run silently downloaded AutoAWQ's own
# large default calibration set.
soup export --format onnx [--onnx-task text-generation|feature-extraction]              # feature-extraction for task: embedding
soup export --model <path> --format gguf --deploy ollama [--deploy-name <name>]          # export, then deploy; the Ollama name defaults to soup-<model-name>

--deploy is validated before anything runs (v0.76.0): it accepts only ollama, and only with --format gguf, gguf-ud, bitnet or tq1_0. Any other combination exits 1 (--deploy is not supported for format 'onnx'. Supported formats for deploy: gguf, gguf-ud, bitnet, tq1_0).

soup merge

Merge LoRA adapter with base model.

bash
soup merge --adapter <path> [--base <model>] [--output <path>] [--dtype <type>]

soup push

Upload model to HuggingFace Hub.

bash
soup push --model <path> --repo <user/repo> [--hub hf|modelscope|modelers]
  [--private] [--collection <owner/slug-hash>] [--message <commit message>]
  • --collection (v0.29) — add the pushed repo to an existing HF Collection

Token resolution for --hub hf: env HF_TOKEN / HUGGINGFACE_HUB_TOKEN > ~/.cache/huggingface/token > ~/.huggingface/token. The legacy --token flag is deprecated and delegates to this chain.

Each other hub authenticates with its own credential, and that is new in v0.75.1. --hub modelscope reads MODELSCOPE_API_TOKEN, --hub modelers reads MODELERS_TOKEN, and neither reads HF_TOKEN any more. Before it, the chain above ran for every hub and the resolved Hugging Face token was handed to the third-party SDK, so pushing to ModelScope or Modelers disclosed a token that is often write-scoped (0.53.10 to 0.75.0). Rotate it if that was you.

soup eval

Comprehensive model evaluation platform.

bash
soup eval benchmark --model <path> [--benchmarks <list>] [--fewshot N] [--batch-size N] [--run-id <id>] [--device cuda|mps|cpu] [--trust-remote-code]
soup eval custom --model <path> --tasks <path> [--trust-remote-code]
soup eval judge --target <pairs.jsonl> [--model <judge-model>] [--provider openai|server|ollama] [--rubric <path>] [--api-base <url>] [--run-id <id>]
soup eval auto [--config <path>]
soup eval compare <run1> <run2>
soup eval leaderboard [--sort-by <benchmark>] [--format table|json|csv]
soup eval human --input <prompts.jsonl> --model-a <path> --model-b <path> [--output <results.json>]
soup eval gate --suite <suite.yaml> [--model <path>] [--baseline <registry://id|scores.json>] [--regression-threshold 0.05] [--write-baseline <file>]
soup eval citation <data.jsonl> [--style bracket|inline|footnote] [--shuffle-seed <n>] [--output <scores.json>]
soup eval against <baseline-run-id> --candidate <run-id> [--metric task_accuracy|refusal_rate|format_validity|p95_latency_ms|custom|aider_polyglot|judge:<model>|benchmark:<lm-eval-task>] [--suite <locked.json>] [--json-only]
soup eval quant-check --before <model|registry://id> --after <model|registry://id> --tasks <tasks.jsonl> [--format table|json|markdown] [--allow-stub]

Notes on the rows that have more to them than the form shows:

  • soup eval benchmark wraps lm-evaluation-harness, so it needs pip install "soup-cli[eval]". --benchmarks is a comma-separated list (default mmlu), --fewshot falls back to each benchmark's own default when you leave it out, --batch-size defaults to 8, --run-id links the results to an existing training run, and --model may be a LoRA adapter directory.
  • soup eval leaderboard reads the experiments database and exits 1 with No eval results found. until soup eval benchmark or soup eval custom has recorded something. --sort-by applies to the table only: --format json and --format csv print the whole leaderboard without sorting by it. A --format value other than json or csv is not rejected; you get the table.
  • soup eval human is interactive, so it needs a terminal. It generates both models' answers to each prompt (a JSONL file whose rows have a prompt field), shows them side by side and asks a, b, tie or q. q ends the session early and the judgments so far are still scored and saved. The result is an Elo table, written with the judgments to --output (default human_eval_results.json). Both model paths must exist locally.
  • soup eval gate exits 0 when the suite passes, 2 when a task fails or regresses against --baseline, and 3 for a setup you can fix: a suite that is missing, outside the working directory or fails its schema, a --baseline that cannot be resolved, a --regression-threshold outside 0.0 to 1.0, --write-baseline without --model, or an unknown flag. It exits 1 when --model cannot be loaded or on an unexpected exception, such as a suite that is not valid YAML. Through v0.75.2 a failed gate and a broken invocation were both 1, so a CI job could not tell them apart by the exit code. Everything else about it, including --write-baseline, is on the eval gate page.
  • soup eval against compares two recorded runs with a paired bootstrap and exits 0 for no regression, 2 for a regression, and 3 for a bad flag, a missing or invalid --suite, or no recorded metric series for either run. The pre-push hook from soup eval gate-install calls it.
  • soup eval quant-check exits 0 on OK or MINOR, 2 on MAJOR, 3 on a usage or input error such as an unknown --format, a path outside the working directory or a .gguf path, and 1 when a model fails to load, which since v0.76.0 stops the command. --allow-stub scores with deterministic stub generators instead; the output then carries a stub marker ("stub": true in the JSON) and an OK means nothing was measured. A --tasks file that is not valid JSONL exits 1. See quant-check.

On soup eval judge, --target is a JSONL file of prompt and response pairs you already generated (every line needs a prompt and a response field; soup infer writes exactly that shape), not a model, and --model names the judge. --api-base is the judge's endpoint: plain http is accepted only for localhost, 127.0.0.1 and ::1, and since v0.75.2 an IP literal that is private, link-local or reserved is refused on https as well, with loopback still allowed. A hostname is not resolved, so the check narrows what a URL can name directly; it does not make an internal host unreachable by name. See what v0.75.2 refuses.

soup deploy

Deploy models to inference runtimes.

bash
# Ollama
soup deploy ollama --model <gguf> --name <name> [--system <prompt>] [--template <tpl>] [--parameter <key=val>] [--yes]
soup deploy ollama --list                # only models whose name starts with soup-
soup deploy ollama --remove <name>       # any Ollama model, by name

# HuggingFace Spaces (new in v0.29)
soup deploy hf-space \
  --model <user/repo> \
  --space <user/space> \
  --template gradio-chat|streamlit-chat

soup deploy hf-space 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. Instead of --template it accepts --template-dir <dir> (custom app.py + README.md, optional requirements.txt, {MODEL_REPO} placeholder; details on Hugging Face Hub integration), plus --private and --yes.

soup infer

Batch inference on a list of prompts. With --task asr (v0.71.32) it transcribes a JSONL of audio clips through a Whisper model or adapter and reports per-row and corpus WER/CER when reference texts are present. See ASR fine-tuning.

bash
soup infer --model <path> --input <path> --output <path> [--max-tokens <int>] [--temperature <float>] [--batch-size <n>] [--cuda-graphs]
soup infer --task asr --model <whisper|adapter> --input <jsonl> --output <jsonl> \
  [--audio-dir <dir>] [--asr-language <lang>] [--asr-task transcribe|translate]

soup migrate

Import config from competing tools.

bash
soup migrate --from llamafactory|axolotl|unsloth <input-file> [--output <path>] [--dry-run]

soup recipes

Browse and use ready-made training configs.

bash
soup recipes list                          # List all 170 recipes
soup recipes show <name>                   # Print recipe YAML
soup recipes use <name> [--output <path>]  # Copy to soup.yaml
soup recipes search <query>                # Search by model/task/size
soup recipes verify [--config <soup.yaml>] [--json] [--templates|--no-templates]   # v0.76.0: can the LoRA adapter attach? checked on the meta device, no weights downloaded

soup recipes verify is one of the eleven commands on the 0 / 2 / 3 taxonomy: 0 when everything that could be checked attaches, 2 when a config cannot attach, 3 for a missing or unparseable --config or a usage error, 1 for a crash. A gated repository or a base that needs trust_remote_code is reported as unverified and does not change the exit code, so an exit 0 can mean nothing was checked. It needs the [train] extra (torch, transformers, peft); without it the command exits 1 before it looks at --config. See Recipes.

Data Commands

bash
soup data inspect <path> [--rows <n>] [--image-dir <dir>]  # View dataset stats (--rows: sample rows to show, default 5; --image-dir is new in v0.76.0)
soup data validate <path> [--format <fmt>] [--min-valid-fraction <0.0-1.0>]  # Check format compliance; exit 3 input error, 2 no usable rows or below the fraction
soup data clean <path> [-o <out>] [-f] [--dry-run] [--min-tokens <n>] [--prune-echo] [--strip-boilerplate] \
  [--repair-code] [--repair-json] [--drop-invalid-json] [--json]   # v0.76.0: sanitise a dataset, never modifies the input
soup data convert <path> --to <fmt> --output <f>  # Convert between formats
soup data merge <f1> <f2> --output <f> [--shuffle]  # Combine datasets
soup data dedup <path> [--threshold <float>]      # Remove duplicates
soup data stats <path>                            # Extended statistics
soup data generate --prompt "..." --count <n>     # Generate synthetic data
soup data generate --provider openai|local|server|ollama|anthropic|vllm  # Multi-provider support (openai is the default)
soup data generate --template code|conversation|qa|preference|reasoning  # Domain templates
soup data generate --quality-pipeline             # Validate + filter + dedup
soup data generate ... --batch-size <n>           # Examples per API call (default 5)
soup data filter <path> [--coherence <f>] [--perplexity <n>] [--ppl-model <id>] [--field <name>] [--score-only]  # Quality filter; perplexity is scored with gpt2 unless --ppl-model says otherwise
soup data sample <path> --strategy random|diverse|hard -n <n> [--pct <0-100>] [--seed <n>]  # Sample subset
soup data split <path> --val 10 --test 10 [--absolute] [--seed <n>]   # Split; --val/--test are integer percentages
soup data split <path> --val 10 --stratify <field>           # Stratify on a field
soup data split <path> --val 10 --stratify-semantic --num-clusters <n>  # TF-IDF + K-Means strata (v0.73.2, needs scikit-learn); not combinable with --stratify
soup data search <query> [--limit <n>] [--sort downloads|likes|lastModified|trending|createdAt]  # Search HuggingFace Hub
soup data preview <dataset_id>                               # Preview remote dataset
soup data download <dataset_id> [--output <path>] [--samples <n>] [--split <split>] [--format alpaca|sharegpt|chatml] [--hub hf|modelscope|modelers]  # Download (default split: train)
soup data register <name> --path <path> --format <fmt>       # Register local dataset
soup data unregister <name>                                  # Remove from registry
soup data registry                                           # List registered datasets
soup data push --input <jsonl> --hf-dataset <user/repo> [--private] [--message <text>] [--hub hf|modelscope|modelers]  # Publish JSONL as a dataset repo (v0.29)
soup data mix --optimize --datasets <a.jsonl,b.jsonl> [--budget 1h] [--num-probes <n>] [--seed <n>] [-o mix_recipe.yaml] [--overwrite]  # Mixture search; WITHOUT --live the proxy is synthetic
soup data mix --optimize --live --base-yaml <soup.yaml> --datasets ...   # --live REQUIRES --base-yaml (real short training runs)
soup data mix --apply <mix_recipe.yaml>                      # Prints the data: block to paste into soup.yaml; writes no dataset
soup data from-traces --logs <logs> --format langchain|openai|soup-serve  # Trace-to-preference (v0.26); for soup-serve --logs is the directory `soup serve --trace-log` writes, and a single file is refused
soup data review <pairs.jsonl>                                # Preview preference pairs (v0.26)
soup data augment --input <jsonl> --strategy rephrase|translate|style     # LLM augment (v0.25)

Experiment Commands

bash
soup runs                         # List all training runs
soup runs show <run_id> [--no-plot]   # Detailed run info + loss curve (--no-plot skips the curve)
soup runs replay <run_id> [--no-plot] # Re-render a finished run's summary and loss curve from its stored metrics
soup runs curriculum-curve <run_id> [--history <curriculum_history.jsonl>] [--width <4-200>]  # Dynamic-curriculum bucket weights (BETA)
soup runs compare <run1> <run2>   # Side-by-side comparison
soup runs delete <run_id>         # Remove from database
soup runs clean [<run_id> | --all] [--dry-run] [--force] [--keep-weights/--no-keep-weights]
                                  # Reclaim checkpoint space: by default drops optimizer and scheduler
                                  # states only; --no-keep-weights deletes whole non-best checkpoints (the one with the
                                  # lowest loss recorded at its step is kept whole; with no recorded loss, the latest)

Other Commands

bash
soup deploy ollama --model <p> --name <n>       # Deploy GGUF to Ollama (both required to deploy)
soup profile --config soup.yaml [--json]       # Estimate memory/speed/GPU (model + task come from the config); --json for scripting
soup adapters list [<dir>]                     # Scan for LoRA adapters (positional, defaults to .)
soup adapters info <path>                      # Adapter metadata
soup adapters compare <a> <b>                  # Side-by-side comparison
soup sweep --config <path> --param key=v1,v2   # Hyperparameter search
soup sweep --config <path> --sweep-config <sweep.yaml>   # v0.76.0: a standalone sweep file; it replaces --param (give one of the two, never both: either mistake exits 1)
soup diff --model-a <p> --model-b <p>          # Compare two models
soup doctor [--nccl] [--disk] [--config soup.yaml]                   # Check system + deps (+ NCCL bandwidth, + media type); --config loads a soup.yaml under the current rules and exits 2 if it cannot load (it stops at the first cross-field refusal, so fix that setting and run it again)
soup quickstart [--dry-run] [--yes]             # One-command demo
soup ui [--port <int>] [--no-browser] [--host <h>] [--public] [--auth-token <t>] [--show-token]
# --public binds 0.0.0.0 and prints a phone QR. A non-loopback bind WITHOUT a valid
# token exits 2, a guard no option of this command reaches (a valid token is always
# generated and a malformed --auth-token exits 1 first). Every private endpoint needs
# Authorization: Bearer; SSE takes a short-lived single-use ticket instead.
# /docs and /openapi.json are loopback-only.
# The launch URL carries ?token= so the first tab authenticates; on a loopback bind the page then
# keeps the session in an HttpOnly cookie, not in sessionStorage or on window (v0.76.0).
# --auth-token must be 16-128 URL-safe characters (A-Z a-z 0-9 _ -); anything else exits 1.
soup tui                                        # Full-screen training dashboard
soup monitor [--refresh <0.25-30>] [--once]     # Live GPU monitor: util / temp / VRAM / power per GPU
soup fetch examples|deepspeed_configs [<name>]  # Fetch a ready-to-edit file from the bundled catalog
soup quantize <model> --to <fmt> [--bits N] [-o <dir>]  # PRINTS the matching `soup export` command and runs nothing
soup plugins list|enable|disable|install        # lists plugins found under the soup_cli.plugins entry-point group, none on a stock install (their code is not imported until enabled);
                                                # enable persists an opt-in in ~/.soup/plugins.json and is where third-party code loads; install explains and exits 2
soup llama cli|mtmd-cli|gguf-split|server|quantize ...  # Proxy to the llama.cpp binaries
                                                        #   closed allowlist; the child env is FILTERED, so
                                                        #   HF_TOKEN / OPENAI_API_KEY / ANTHROPIC_API_KEY and
                                                        #   every other secret are dropped before exec. What
                                                        #   is forwarded: PATH / HOME / USER / USERPROFILE,
                                                        #   TMP / TEMP, TERM / COLORTERM, LANG / LC_ALL /
                                                        #   LC_CTYPE, plus LLAMA_CPP_HOME, GGML_CUDA,
                                                        #   GGML_METAL and OMP_NUM_THREADS.
soup bench infer <model> [--base <m>] [--backend auto|transformers|mlx]  # Inference speedometer; `soup bench <model>` still runs it
                         [--max-tokens <n>] [--num-prompts <n>] [--prompts-file <p>]
                         [--p50] [--p95] [--cuda-graphs]              # tail-latency percentiles; --cuda-graphs is experimental
soup bench train [--config soup.yaml] [--steps 20] [--warmup 3] [-o bench-train.json]   # v0.76.0: fails when the model was not actually training
soup rewind [RUN_ID] [--step <n>] [--top 10] [--json <path>] [--no-preview]              # v0.76.0: the dataset rows behind a loss spike
soup version [--full] [--json]                  # Show version info; --json prints one JSON object (version, python, platform, plus installed library versions with --full)
soup ra-dit -r <retriever.yaml> -g <generator.yaml> [--retriever-model <m>] [--timeout <60-21600>] [--plan-only]
                                                # RA-DIT two-stage run. BOTH recipes are required and cwd-contained;
                                                # --timeout is per stage (default 21600 s); --retriever-model skips the registry auto-link
soup merge-sharded-fsdp-weights <shard_dir> -o <out.safetensors> [--plan-only]
                                                # consolidate pytorch_model_fsdp_*.bin shards into one file; -o is required and under the cwd
soup delinearize-llama4 <source_dir> -o <target_dir> [--num-experts <n>] [--model-id <id>] [--plan-only]
                                                # reshape fused expert weights to 3-D; -o is required and under the cwd;
                                                # --num-experts defaults to num_local_experts in config.json
soup deploy autopilot --target <profile> [--base <model>] [--recipe-out deploy_autopilot.yaml] [--script-out deploy_autopilot.sh] [--output-dir ./output]
soup deploy autopilot --target <profile> --measure --tasks <tasks.jsonl> [--measure-candidates 4bit,gptq,awq]
soup deploy autopilot --list                    # --target is required unless you pass --list; both output paths must be under the cwd
soup local-rl train --model <hf-id|path> [--db local_rl.db] [--train-method dpo|kto|orpo] [--once] [--min-pairs 10] [-o local_rl_adapter]
soup local-rl train --model <hf-id|path> [--scheduler-dir local-rl-scheduler] [--hour 3] [--minute 0] [--backend ollama|mlx]
                                                # without --once it only writes a systemd/launchd scaffold; it never runs systemctl or launchctl
soup can pack --entry-id <registry-ref> -o <out.can> [--author <handle>] [--description <text>] [--attest <in-toto.json>]...
soup can fork <source.can> -o <out.can> --modify training.lr=5e-5 [--author <handle>]
soup can run <file.can> [--yes] [--deploy] [--extract-dir <dir>] [--env-capture <file>]
soup can publish <file.can> --hf-hub <user/repo> [--private] [--message <text>]

soup doctor --disk is opt-in because the probe costs roughly 9 seconds cold and 2.4 seconds warm on Windows, and with several physical disks it reports the worst one. Since v0.73.3, on Linux, it may also write a 64 MiB scratch file named .soup-diskprobe-* into the directory it is probing (the current directory for soup doctor --disk; a training run probes the shard directory instead), read it back with O_DIRECT to time a sequential read, and delete it, because on a disk whose rotational flag cannot be trusted the media type is settled by a measured read rather than by the flag. On Windows it only runs the disk query. It exists because the streaming disk tier requires NVMe and refuses a SATA SSD, a spinning disk or media it cannot identify, rather than guessing and thrashing for hours.

soup quantize quantizes nothing: it prints the soup export command to run. For --to gguf that command carries --quant q4_K_M (and q8_K_M for --bits 8), which soup export refuses, because its --quant is case-sensitive and accepts only q4_0, q4_k_m, q5_k_m, q8_0, f16 and f32: retype it in lower case, and use q8_0 for 8 bits. --to gptq and --to awq print a --bits form to which you must add --calibration-data, and since v0.76.0 they print a Deprecated: line after it: both formats are deprecated and upstream says they are removed in v0.77.

soup monitor reads nvidia-smi on NVIDIA systems. On Apple Silicon it reads /usr/bin/powermetrics through sudo -n, which never prompts for a password, so run sudo -v first or the command exits 1 and tells you to.

v0.54 — Pre-flight Decision

bash
soup advise run <data.jsonl> --goal "<task>"          # Classify task + rank PROMPT_ENG/RAG/SFT/DPO/GRPO
soup advise run <data.jsonl> --probe                  # ROI estimates, heuristic: derived from the shape of the data, no model loaded
soup advise run <data.jsonl> --probe-model <id> [--probe-device cpu|cuda]   # LIVE probe: loads the model, LoRA-trains on a held-out slice
soup advise run <data.jsonl> --record                # Append to ~/.soup/advise_history.jsonl
soup advise explain                               # Print full rubric
soup advise compare [--limit N]                   # Prior verdicts, newest first

v0.55 — Eval Design

bash
soup eval design <data.jsonl> --goal "..."        # Goal-conditioned suite (TF-IDF + dispatch)
soup eval discover <data.jsonl>                   # Greedy farthest-first canaries + memorization probes
soup eval lock <design.json>                      # SHA-256-pin the suite as an artifact
soup eval coverage <design.json> --task factual_lookup|style_shaping|format_conversion|reasoning|tool_use|summarization|classification  # Gap analysis vs v0.54 taxonomy (--task is required)
soup eval gate-install --baseline <run-id> [--suite <locked.json>] [--hook-path <file>] [--force]   # Install .git/hooks/pre-push regression gate (suite defaults to evals/locked.json)

v0.56 — Diagnose (Model Report Card)

bash
soup diagnose <run-id> [--output <report.json> [--attach-to-registry <id>]] [--allow-not-run] [--holdout <jsonl>]
                                                    # 7 failure-mode probes + OK/MINOR/MAJOR/NOT_RUN + SVG badge. Exit 2 on MAJOR, exit 3 on NOT_RUN
                                                    # (a requested probe did not run) unless --allow-not-run. --attach-to-registry needs --output and exits 1 if the attach fails
soup train --diagnose-gate <evidence>               # After training: exit 2 if the evidence you supply has a MAJOR mode, exit 3 if a requested mode did not run (the adapter is already saved)

v0.57 + v0.67 — Adapter VCS

bash
# v0.57 — git for LoRA
soup adapters diff <a> <b>                            # Per-layer Frobenius + relative drift + SVD effective rank
soup adapters merge <a> <b> [c...] -o <out> [--strategy linear|ties|dare|svd|cmaes] [--weights 0.5,0.5] [--density 0.2] [--rank <n>]
soup adapters merge <a> <b> [c...] -o <out> --strategy cmaes --eval <suite> [--budget 1h] [--population 8] [--max-generations 20]
                                                       # v0.67 CMA-ES evolutionary search: --eval is required; --population is 2-256,
                                                       # --max-generations 1-10000, --budget 60s-24h; --density is for ties/dare, --rank for svd
soup adapters blame <adapter-dir> --dataset <d> --layer <l> --budget 5m --shards 4
soup adapters branch <name> -c <config.yaml> --base <model> [--dataset <data>] [--attach-to-registry <id>]
soup adapters branch <name> --from-registry <id>       # derive config, base model and dataset hash from a registry entry instead
soup adapters checkout <name> -o <out.yaml>
soup adapters branches

# v0.76.0 additions
soup adapters check <adapter-dir> [--json]            # is the adapter trained at all? exit 0 alive, 2 inactive, 1 unreadable
soup adapters audit <adapter-dir> --config <soup.yaml> [--json]   # did the run do what the config asked? exit 0 agree, 2 diverged (also a Click error), 1 adapter or config unreadable

# v0.67 additions
soup adapters pr <title> --base-sha <64hex> --adapter <path> \
  --eval <eval_delta.json> --samples <sample_diffs.json> \
  [--dataset-diff <diff.txt>] [--format markdown|json] --output <PR.md>   # GitHub-shaped PR markdown (--dataset-diff is a text file you supply)
soup adapters bisect <ckpt1> <ckpt2> ... \
  --eval-command "soup eval gate --suite <gate.yaml> --baseline <baseline.json> --model {ckpt}"
                                                       # Binary-search history for regression boundary (exit 2 on BROKEN_AT; the eval command must exit non-zero on a bad checkpoint, which soup eval custom never does; soup eval gate fails with 2, and a usage mistake in the command, which is 3, also reads as a failed checkpoint)

# Shared run lockfile (v0.67)
soup lock write --base-model <id> --base-sha <64hex> --dataset-sha <64hex> \
  [--env-hash <64hex> | --env-lock <soup-env.lock>] [--output soup.lock]    # env hash derived from soup-env.lock when omitted
soup lock show [soup.lock]
soup lock check --base-model <id> --base-sha <64hex> --dataset-sha <64hex> \
  --env-hash <64hex> [soup.lock]                       # Exit 2 on drift; 3 if the lock is missing or unparseable or a flag is wrong

v0.58 — Production Data Flywheel

bash
soup loop init <served-model> --eval <suite> --baseline registry://<id> \
  --monthly-budget 50usd --max-runs-per-day 3
soup loop watch                                   # Long-running daemon (SIGTERM-safe)
soup loop status
soup loop pause / soup loop resume
soup loop canary <adapter> --traffic 5% --autoroll-on-regress
soup loop replay [<iter-id>]

v0.59-v0.62 — Governance, Supply Chain, Unlearn, RAG

bash
# v0.59 — Governance & Provenance
soup bom emit --name <m> --version <v> --base-model <hf> --base-sha <64hex> \
  --config-sha <64hex> [--data-sha <64hex>] --task <t> --license <spdx> --format cyclonedx|spdx|both \
  --output <prefix> [--attach-to-registry <id>]   # v0.73.3; needs --output
soup attest emit --stage extract|train|eval|export|publish \
  --subject <artifact> --sha <sha> --builder <id> --invocation <cmd> \
  --sign unsigned|ed25519|sigstore [--key <priv.pem>] [--interactive-oidc] \
  --output <path> [--attach-to-registry <id>]     # ed25519 needs --key (or SOUP_SIGNING_KEY) and adds the .sig sidecar
# --sign sigstore (v0.76.0) is keyless signing through Fulcio and Rekor. It needs
# pip install "soup-cli[sigstore]" and --output, and it writes the Sigstore bundle into the
# .sig sidecar. It never falls back to an unsigned statement: if it cannot sign it exits
# non-zero (2 for a missing extra, a missing --output, or no ambient credential when the Sigstore
# trust root can be loaded; 1 for a failure at Fulcio or Rekor, or a trust root that cannot be
# loaded, for example offline) and writes nothing. CI uses an ambient OIDC credential;
# --interactive-oidc allows the browser flow instead.
soup attest verify <statement> --signature <statement.sig> \
  [--public-key <pem> | --cert-identity <san> --cert-oidc-issuer <url>]   # exit 0 valid, 1 verifier unavailable, 2 usage, 3 invalid or policy mismatch
soup audit-log tail / rotate
soup train --annex-xi <out.md> --repro-receipt <out.json>

# v0.60 — Supply Chain Security
soup adapters scan <dir>                          # Spectral LoRA backdoor scanner (exit 0/1/3)
soup adapters sign <dir> [--backend unsigned|ed25519|sigstore] [--key <k> | --generate-key <out.pem>] [--interactive-oidc]   # Merkle-root signing (--strict is on verify); sigstore is keyless and needs [sigstore]
soup adapters verify <dir> [--strict] [--public-key <pem>] [--cert-identity <san> --cert-oidc-issuer <url>]   # Merkle-root tamper detection; a Sigstore-signed adapter needs both cert options
soup adapters check-safetensors <dir> --strict    # Refuse pickle/.bin/.pt
soup adapters merge <a> <b> -o <out> --license <spdx-a> --license <spdx-b>   # SPDX-license gate: one --license per adapter, same order; omit them to auto-detect
soup airgap-bundle --model <dir> --output <bundle.tar> [--dataset <dir>]... [--wheel <dir>]... [--kernel <dir>]...   # --output is a tarball path, the others are directories

# v0.61 — Unlearning & Knowledge Edit
# (configured via task: unlearn in soup.yaml)
soup eval unlearning <run-id> --benchmark tofu|muse|wmdp --evidence <evidence.json>   # runs no benchmark itself; with no --evidence every metric is a neutral OK
soup edit set --base <m> --method rome|memit|alphaedit|grace --subject "..." --target "..." [--plan-only]
soup edit diff run_before run_after --probes probes.jsonl --top-k 10 \
  --before-model ./model-before --after-model ./model-after
# The two ids are labels for the report, not registry lookups. With --before-model and
# --after-model it generates the diff live and --probes is then required (exit 2 without it).
# With neither model it prints an empty validated table and exits 0.

# v0.62 — RAG & Activation Steering
soup steer train --base <m> --method caa|iti|repe --name <id> --pairs <jsonl>
soup steer apply --name <id> --strength <float>   # |strength| ≤ 10 enforced
soup steer list                                   # Registry entries: soup steer train --registry-id <id> (needs --output) records the vector there since v0.76.0

v0.63 — Production Trace Ecosystem

bash
soup ingest --source langfuse|langsmith|helicone|openpipe|otel|openai-stored \
  --logs <jsonl> --output <out.jsonl>
soup ingest --source langfuse --pull [--since 7d] [--max-pages 100] \
  [--allow-private-host] --output <out.jsonl>
# --pull fetches from the Langfuse API instead of --logs, and is langfuse-only.
# Credentials come from LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY, never a flag
# (LANGFUSE_BASE_URL or LANGFUSE_HOST for a region or self-hosted instance; it must be
# https://, and --allow-private-host is what lets it be a private or loopback address).
# --since max 365d; --max-pages max 10000, and hitting it exits 1 writing nothing.
soup prune-prompt --input <jsonl> --output <out.jsonl> --min-frequency 0.95
soup data active-sample --input <jsonl> --budget N
soup ab --input <jsonl> --metric latency|judge_score|retry_rate \
  [--alpha 0.05] [--effect-size 0.1] [--slack-url <url>] [--discord-url <url>]
# Two-sided, variance-robust sequential test (v0.76.0): it reports whether the treatment is better
# or worse, and can be re-run after every new row. --beta is retired: it is ignored with a warning
# that says v0.77 will refuse it. Exit 0 on every decision, so read the verdict, not the exit code.
soup drift-alarm --reference <ref.jsonl> --live <live.jsonl> --threshold 0.2 \
  [--slack-url ...] [--discord-url ...]                # Exit 3 on drift

v0.64 — Pre-flight & Tooling

bash
soup tunability --dataset <path> [--candidates <names>] \
  [--probe-steps 100] [--holdout-size 64] [--output <path>] [--plan-only] [--list]
soup plan --config <soup.yaml> [--state ./soup.tfstate]
soup apply --config <soup.yaml> [--state ./soup.tfstate] [--dry-run]
soup env lock [--output ./soup-env.lock]
soup env status [--lock ./soup-env.lock]
soup env check [--lock ./soup-env.lock]                # Exit 3 on ABI drift or a violated declared bound
soup env fix [--lock <path>] [--format uv-pip|requirements] [--output <path>]   # print an install plan; never installs
soup completions bash|zsh|fish
soup license-advisor --target b2c|defense|embedded \
  [--license <spdx>] [--monthly-active-users <N>]      # Exit 3 on block

v0.65 — Eval Depth (Failure Modes 6 → 10)

bash
soup eval behavior <run-id> --battery xstest|harmbench|jailbreakbench|elephant|syceval \
  (--evidence <responses.json> | --base-model <id> [--adapter <path>] [--device cuda|cpu]) [--output <report.json>]
soup eval capability <run-id> --suite full|fast|math|code [--output <report.json>]
soup eval checklist <spec.yaml> --evidence <responses.json> [--output <report.json>]
# --evidence is required on both: without it (and, for behavior, without --base-model) the
# command exits 3. Through v0.75.2 it printed a neutral OK report and exited 0.
soup eval irt-subset <responses.jsonl> --size full|small|tiny [--model 1pl|2pl|3pl] [--output <plan.json>]   # --model defaults to 1pl (Rasch); 2pl adds discrimination, 3pl a guessing floor

v0.66 — Post-train X-rays

bash
soup probe sae-diff <sae.safetensors> <pre_acts.json> <post_acts.json> \
  [--top-k 20] [--output <report.json>]
soup probe sleeper <base> [--evidence <activations.json>] [--output <result.json>]
soup probe interference <losses.json> [--output <matrix.json>]   # Exit 2 if worst ≥20%
soup probe pack <base> [--list] [--output <manifest.json>]

v0.69: Data engineering

bash
soup build <manifest.yaml> [-o <dir>] [--dry-run]       # dbt-style DAG of dataset transforms; -o defaults to build_out
soup expect <data.jsonl> <suite.yaml> [--judge <url>]   # expectations suite over a JSONL dataset
# soup expect exits 0 when the suite passes, 2 when an expectation fails, 3 when the suite
# or the data is rejected or a flag is mistyped, and 1 when the judge cannot be reached.

Both are documented on Data engineering pro, including the judge expectation, which needs --judge or args.judge.

v0.71.27 — Fine-tune Doctor

bash
soup data doctor <data.jsonl> --model <id|path> \
  [--show-mask N] [--train-on-responses-only | --no-train-on-responses-only] \
  [--train-on-eot] [--train-on-messages-with-train-field] [--mask-history | --no-mask-history] \
  [--output <report.json>]
# 8 chat-template checks (eos_in_labels, bos_duplication, truncation_risk, …)
# OK/MINOR/MAJOR verdict · exit 0 OK/MINOR · exit 2 MAJOR

soup data lint <data.jsonl> [--model <id|path>] [--output <report.json>]
# Preference-data linter for dpo/orpo/simpo/ipo/bco/kto:
# length_bias (Cohen's d), label_imbalance, near_duplicates,
# identical_pairs, prompt_leak
# exit 0 OK/MINOR · 2 MAJOR · 3 a missing path, an empty file, data that is not preference
# data (use soup data doctor) or an unwritable --output · 1 a format that cannot be detected

v0.71.28 — MCP Server

bash
pip install "soup-cli[mcp]"
soup mcp serve                    # stdio · 14 read-only tools
soup mcp serve --allow-mutating   # also run train_start / export (planning only)
soup mcp serve --allow-execute    # + train_execute / export_execute, token-gated (v0.73.3)

v0.71.29 — Model shrink

See soup shrink.

bash
soup shrink --model <id|path> --calib calib.jsonl --drop-ratio 0.25 [--drop-layers N] \
    [--heal heal.jsonl --heal-steps 200] [--tolerance 0.10] [-o out/] [--device cpu] \
    [--attach-to-registry <id>] [--plan-only]   # exit 0 = SHIP, 2 = DON'T SHIP, 1 = error

v0.71.30 — PRM-guided GRPO + rollout envs

See PRM-guided GRPO.

bash
soup train --config grpo.yaml   # training.prm_reward: <path|id> · training.prm_aggregate: min|prod|last
soup recipes use grpo-env-calculator      # or grpo-env-retrieval-qa / grpo-env-guess-number

v0.71.31 — Judge in the loop

See Online DPO and best-of-N & Evolve.

bash
soup train --config online_dpo.yaml   # task: online_dpo; training.online_dpo_judge OR training.reward_model (exactly one)
soup data best-of-n --base <m> --prompts p.jsonl --n 8 --judge <url> -o sft.jsonl [--emit-pairs dpo.jsonl]
soup data evolve --input seeds.jsonl --provider ollama|vllm --model <m> --strategy depth|breadth --rounds N -o out.jsonl
soup ship --task-mode pairwise --judge-model <url>     # swap-debiased judge win-rate vs a 0.5 base

v0.71.32 — ASR (Whisper)

See ASR fine-tuning.

bash
pip install "soup-cli[train,audio]"
soup recipes use whisper-tiny-asr          # task: asr · format: asr · fits a 4 GB GPU
soup infer --task asr --model ./out --input eval.jsonl --output preds.jsonl \
    [--audio-dir ./data/audio] [--asr-language en] [--asr-task transcribe]

v0.71.33 — Speculative-decoding drafts

See soup draft.

bash
soup draft measure --target <m> --draft <d> --prompts p.jsonl   # acceptance + real tok/s
soup draft measure ... --min-acceptance 0.6 -o report.json      # exit 2 below the floor (CI)
soup draft measure ... --sweep-k 1,2,3,5,8                      # v0.76.0: also time assisted generation at each draft length (at most 8 values, each 1 to 64)
soup draft distill --target <tuned> --draft-base <tiny> --data d.jsonl -o draft/
soup draft distill ... --steps N --device cpu --force --plan-only
soup draft list                                                 # what --auto-spec will pick up

v0.71.34 — Adapter algebra + LISA

See adapter algebra and LISA.

bash
soup adapters arithmetic "coder + 0.5*math - toxic" \
    --adapter coder=<p> --adapter math=<p> --adapter toxic=<p> -o <out> \
    [--allow-unscanned] [--allow-cross-base]

# LISA is config-side (sft · transformers · text · quantization: none)
soup train --config sft.yaml   # training.lisa_enabled: true
                               # [lisa_num_layers] [lisa_interval_steps] [lisa_reset_optimizer]

v0.71.35 — Compliance pack

See compliance pack.

bash
soup init --template hipaa|soc2|eu-ai-act|sr-11-7   # regulation-shaped starting config
soup card <registry-id> -o MODELCARD.md            # provenance-carrying HF model card
soup push --model ./out --repo you/m --card <registry-id>   # upload it as the README (HF only)
soup ci init [--data d.jsonl --suite s.yaml --evidence ev.json] \
    [--branch main] [--python 3.11] [--force]      # writes .github/workflows/soup-gate.yml

v0.72 — Layer streaming

Streaming is turned on by config keys, not CLI flags: there is deliberately no --stream-layers. See layer streaming, scaling a streaming run and preference losses over streaming.

bash
# The whole surface is in soup.yaml, under training:
#   stream_layers: true      # enable it (BETA)
#   stream_source: auto      # auto | ram | disk   (NVMe-class only; ram refuses rather than falling back)
#   stream_disk_kind: nvme   # v0.73.3: override the detected media type
#   stream_pin: false        # v0.74.0: force the pageable store (true refuses on a CUDA target if the
#                            #          box cannot page-lock it; since v0.76.0 that includes the disk tier's staging)
#   stream_read_ahead: 2     # v0.76.0: disk tier only, 1-8, the read-ahead depth of the background reader
#   stream_ngram_source: auto # v0.74.0: Qwen4-Exp external N-gram table, auto | ram | disk
#   stream_buffers: 2        # 2-8, VRAM buffers in the pool
#   quantization: 4bit       # or none; nothing else streams
# task: sft | dpo | orpo | simpo | kto             (kto needs batch_size >= 2, on every run since v0.76.0)
soup train --config soup.yaml
soup train --config soup.yaml --resume auto       # unblocked for streaming in v0.72.3 (load-side); the old
                                                  # torch < 2.6 resume block is gone since v0.75.0 raised the floor

soup doctor --disk    # report the media type behind the disk tier (NVMe / SATA SSD / HDD /
                      # Unknown). Opt-in: the probe costs about 9 s cold, 2.4 s warm

Since v0.76.0 the disk tier reads on a background thread through direct I/O and can stripe across several NVMe drives with the SOUP_LAYER_STREAM_STRIPE_DIRS environment variable. With it set, plain soup doctor lists each entry as a Stripe root row under System Resources and adds an issue when one is refused, and soup doctor --disk also applies the NVMe rule to them. The disk tier has the rules and what was measured.

v0.74.0 — the frozen base, the stack, and a listener

See v0.74.0: loaded in fp32.

bash
# MCP over the network. Both transports serve the same 18-tool registry as stdio.
soup mcp serve --transport sse --host 127.0.0.1 --port 8765 --auth-token "$TOKEN"
soup mcp serve --transport http --host 127.0.0.1 --port 8765 --auth-token "$TOKEN"
# --allow-execute is REFUSED with either network transport; --host/--port/--auth-token are
# refused under --transport stdio rather than silently ignored.
soup mcp runs reconcile --expunge-launching [--older-than-seconds N]   # recover capacity from stale launching rows; N defaults to 300 (min 1)

soup eval aider --model <aider-model-id> --output <results-dir> [--run-id <id>]   # Aider Polyglot ([aider])
# --model and --output are required. --model is an Aider/LiteLLM id such as
# openai/gpt-4.1, not a local checkpoint. --output is a DIRECTORY under the cwd: it
# receives Aider's own result files and Soup's soup_result.json (a name like report.json
# would just become a directory, and an existing file at that path is refused).
# Docker must be running; the model's generated code executes inside the container.
# Also: --exercises-dir, --image, --threads, --num-tests, --timeout, --allow-host-services.
# The [aider] extra is NOT enough to run it: you also need the official Aider
# benchmark image, source-built from an Aider checkout (./benchmark/docker_build.sh),
# and a clone of polyglot-benchmark. --allow-host-services lets untrusted
# model-generated code reach other services listening on the host.
soup train --config soup.yaml --cloud lambda   # plan-only by default, like --cloud modal
soup export --model ./output --format gptq --calibration-data cal.jsonl   # now required (gptq is deprecated in v0.76.0)
soup serve --model ./output --host 0.0.0.0 --tool-auth-token "$TOKEN"     # exit 2 without the token
soup sweep --config soup.yaml --param lora_r=8,16   # an unknown --param now exits 1, before
                                                    # the first arm, instead of running a grid

Two scope notes on those lines. --tool-auth-token is the bearer token for the tool, /v1/thumbs and /v1/adapters routes of the transformers backend; generation, /v1/models, /health and /metrics carry no token on any backend, so a non-loopback bind publishes the model to whoever can reach the port, and an authenticating reverse proxy belongs in front of it. The vLLM, SGLang and MII backends do not read the token at all. Serving has the route-by-route table. And --param takes a shortcut name (lora_r, lr, epochs, batch_size and so on) or the full path (training.lora.r); a bare lora.r is not either, and every arm of such a sweep fails when its config is built, while the command still exits 0 with failed rows.

v0.76.0: new commands and flags

The six commands that are new in v0.76.0, plus soup bench infer (the old soup bench <model>, now a subcommand of the new bench group), as the v0.76.0 binary prints them:

bash
soup rewind [RUN_ID] [--step <n>] [--top 10] [--json <path>] [--no-preview]
soup bench infer <model> [--base <m>] [--backend auto|transformers|mlx] [--max-tokens <n>] [--num-prompts <n>] [--prompts-file <p>] [--p50] [--p95] [--cuda-graphs]
soup bench train [--config soup.yaml] [--steps 20] [--warmup 3] [-o bench-train.json]
soup data clean <path> [-o <out>] [-f] [--dry-run] [--min-tokens <n>] [--prune-echo] [--strip-boilerplate] \
  [--repair-code] [--repair-json] [--drop-invalid-json] [--json]
soup recipes verify [--config <soup.yaml>] [--json] [--templates|--no-templates]
soup adapters check <adapter-dir> [--json]
soup adapters audit <adapter-dir> --config <soup.yaml> [--json]
CommandWhat it answersExit codesPage
soup rewindWhich dataset rows were in the batch behind a loss spike0 a report printed, including No loss spikes recorded.; 1 nothing to read; 2 a refused --json pathsoup rewind
soup bench inferThe old soup bench <model> inference benchmark; soup bench <model> still routes hereunchangedsoup bench
soup bench trainWhether the model actually trained for a fixed number of steps; it fails when it did not0 valid; 1 not training, a config outside its scope or the [train] extra missing; 2 a refused -o path or a Click errorsoup bench
soup data cleanSanitise a dataset (control characters, zero-width spaces, BOMs by default; heuristic repairs are opt-in) and never modify the input0 done, including dropped rows and --dry-run; 1 an input or output refused; 2 a mistyped optionClean a dataset
soup recipes verifyCan this recipe's LoRA adapter attach, checked on the meta device with no weights downloaded0 everything checkable attaches; 2 a config cannot attach; 3 a missing or unparseable --config, or a usage error; 1 a crash, or torch, transformers or peft failing to importRecipes
soup adapters checkIs this adapter trained at all0 alive; 2 inactive; 1 the adapter could not be readAdapter check
soup adapters auditDid the run use the settings in this soup.yaml0 no divergence (unknown rows do not fail); 2 diverged, and also a forgotten --config or a mistyped flag; 1 the adapter, the config or the recorded settings cannot be readAdapter check

Limits worth knowing before you script them, each stated as upstream behaves at v0.76.0:

  • soup rewind --json writes no file when the run had no spike (it prints No loss spikes recorded. and exits 0), and writes NaN or Infinity, which is not strict JSON, for a non-finite loss.
  • soup adapters check needs the safetensors package and NumPy although it does not need PyTorch, exits 1 on an MLX adapter (the MLX trainer names its file adapters.safetensors), and reports inactive for a non-LoRA PEFT adapter such as VeRA. Its scope is LoRA adapters saved by the transformers path.
  • soup recipes verify exits 0 when everything it could check attaches, which can mean everything was unverified (a gated repository, or a base that needs trust_remote_code).
  • soup data clean ignores --strip-boilerplate and --prune-echo on dpo and kto rows without saying so.

Existing commands that changed

CommandWhat changed in v0.76.0Page
soup train--nodes, --node-rank, --master-addr, --master-port for multi-node launches; --gpus returns the launcher's exit code on Windows too; --diagnose-gate exits 3 on a NOT_RUN probe; --dry-run on a config whose data loads zero train rows exits 1Multi-GPU
soup infer--batch-size N (opt-in, default 1) and --cuda-graphs (experimental, needs PyTorch 2.14 or newer)soup bench
soup serve--auto-quant refuses with exit 2Inference server
soup export, soup quantizeAWQ and GPTQ are deprecated, removal tracked for v0.77; --deploy is validated before anything runsModel export
soup expect--judge <url>; exit 2 means the suite failed, exit 3 means the suite or data was rejected, exit 1 means the judge was unreachableData engineering pro
soup eval gate, soup eval against, soup lock check, soup data validate, soup data lint, soup shipExit codes moved to 0 pass, 2 gate failure or drift, 3 usage or input errorExit codes
soup eval behavior, soup eval checklist--evidence is required (on behavior, unless --base-model is given); without it they exit 3Eval depth
soup eval quant-check--allow-stub; a model that fails to load stops the command instead of silently scoring a stubQuant check
soup eval benchmark, soup eval custom--trust-remote-code (default deny); soup eval benchmark exits 1 with Nothing measured; nothing saved to experiment tracker. when no benchmark produced a score (it used to exit 0)Experiment tracking
soup diagnose--allow-not-run, --holdout; the NOT_RUN verdict exits 3soup diagnose
soup abTwo-sided, variance-robust sequential test that reports better or worse; --beta retired (ignored with a warning that names v0.77 as the version that will refuse it)Trace ecosystem
soup draft measure--sweep-k; reports the break-even acceptance rate and the best draft lengthsoup draft
soup sweep--sweep-config; --param is no longer required (give one of the two)Experiment tracking
soup adapters sign, soup adapters verify, soup attest emit, soup attest verifySigstore keyless signing (--backend sigstore, --sign sigstore, --interactive-oidc, --cert-identity, --cert-oidc-issuer); needs pip install "soup-cli[sigstore]"Supply chain security
soup autopilot--model, --data and --goal are asked for on a terminal (the goal defaults to chat); when standard input is not a terminal a missing one exits 2Autopilot
soup data inspect, soup data doctor, soup data score, soup data canary insert, soup data recipe--image-dir; --mask-history; --benchmark-file; --format alpaca|sharegpt|chatml; --provider ollama|anthropic|vllm with --model, --base-url and --offlineData tools
soup edit set, soup steer train, soup eval unlearning, soup diagnose--registry-id and --attach-to-registry record the result (they need --output) and exit 1 when the attach failsUnlearning and editing
soup pluginsPlugins are discovered from the soup_cli.plugins entry-point group, enable persists an opt-in, install exits 2 and installs nothingPlugins
soup uiThe launch URL carries ?token=; on a loopback bind the page keeps its session in an HttpOnly cookieWeb UI
soup doctorGPU architecture and precision block, MLX panel, --configInstallation
soup reward stress--attacks default adds wrapped_junk and answer_sprayReward Forge
soup local-rl train--backend is accepted for compatibility and has no effectAnti-trend insurance

Every top-level command

The 78 top-level commands of the v0.76.0 binary, with the page that documents each. A command marked "this page" has its forms above; the rest are documented in depth elsewhere and are not repeated here.

CommandWhat it doesDocumented on
soup initCreate a soup.yaml from a wizard or a templateConfiguration
soup trainStart a training run from a soup.yamlTraining methods
soup chatChat with a trained model in the terminalthis page
soup costEstimate training cost in USD across cloud providersObservability
soup pushPush a model to Hugging Face Hub or another hubHub integration
soup exportExport to GGUF, ONNX, TensorRT-LLM, AWQ, GPTQ, BitNet, tq1_0 or TorchAOModel export
soup mergeMerge a LoRA adapter into its base modelthis page
soup cardGenerate a model card from a registry entryCompliance pack
soup migrateImport a LLaMA-Factory, Axolotl or Unsloth configMigration
soup serveOpenAI-compatible inference serverInference server
soup sweepGrid or random hyperparameter searchExperiment tracking
soup diffCompare two models' outputs on the same promptsExperiment tracking
soup inferBatch inference over a JSONL of prompts, or Whisper transcriptionthis page; soup bench for --batch-size and --cuda-graphs
soup profileEstimate memory, speed and GPU needs before trainingthis page
soup doctorCheck dependencies, GPU and a configInstallation
soup quickstartCreate sample data and a config, then train a small demoInstallation
soup uiLaunch the Web UIWeb UI
soup autopilotPick hyperparameters from a model, a dataset and a goalAutopilot
soup historyLineage tree of the registry entries that share a nameModel registry
soup whyExplain a run's training anomalies in plain EnglishObservability
soup rewindName the dataset rows behind a loss spike (new in v0.76.0)soup rewind
soup tuiFull-screen terminal dashboard of runs, metrics and logsObservability
soup monitorLive GPU monitor: utilisation, temperature, VRAM, powerthis page
soup fetchFetch a ready-to-edit file from the bundled catalogthis page
soup quantizePrint the matching soup export command; quantizes nothingthis page
soup merge-sharded-fsdp-weightsConsolidate FSDP shard files into one safetensors filethis page
soup delinearize-llama4Reshape Llama 4 fused expert weights to 3-Dthis page
soup diagnosePost-training report card over the failure-mode probessoup diagnose
soup airgap-bundleTarball of a model, datasets, wheels and kernels for an air-gapped hostSupply chain security
soup ingestImport traces from Langfuse, LangSmith, Helicone, OpenPipe, OTel or OpenAI stored completionsTrace ecosystem
soup prune-promptStrip a system-prompt prefix shared across training rowsTrace ecosystem
soup abTwo-sided sequential A/B test on latency, judge score or retry rateTrace ecosystem
soup drift-alarmRolling-KL drift alarm with optional Slack or Discord webhookTrace ecosystem
soup tunabilityProbe-train small bases and rank them by eval delta, cost and licensePre-flight and tooling
soup planRender a pre-flight plan and write soup.tfstatePre-flight and tooling
soup applyRun the planned training, refusing on drift between soup.yaml and soup.tfstatePre-flight and tooling
soup completionsEmit a bash, zsh or fish completion scriptPre-flight and tooling
soup license-advisorRecommend a license-clean base for a deploy targetPre-flight and tooling
soup compileCompile a DSPy or GEPA prompt program against an eval suiteAnti-trend insurance
soup distill-promptDistill prompt-heavy traces into a small fine-tune planAnti-trend insurance
soup compile-toolsOptimise tool schemas and descriptionsAnti-trend insurance
soup apple-adapterConvert HF, MLX and Apple FoundationModels adaptersAnti-trend insurance
soup builddbt-style DAG of dataset transformsData engineering pro
soup expectRun an expectations suite against a JSONL datasetData engineering pro
soup shrinkDepth-prune a model, with an optional distill-healsoup shrink
soup versionShow the Soup versionthis page
soup ciWrite a fine-tuning CI gate workflowCompliance pack
soup dataDataset tools: inspect, convert, merge, dedup, validate, clean and moreData tools
soup deployDeploy to Ollama or Hugging Face Spaces, or plan a deployModel export
soup runsExperiment tracking: list, show, compare, clean runsExperiment tracking
soup evalBenchmarks, custom evals, LLM judge, gates and eval-depth commandsExperiment tracking, Eval gate
soup adaptersAdapter management: list, diff, merge, sign, verify, check, audit and moresoup adapters
soup recipesReady-made configs: list, show, use, search, verifyRecipes
soup benchBenchmark a model: inference speed or a fixed-step training runsoup bench
soup registryModel registry: push, list, show, diff, search, promote, deleteModel registry
soup spectrumSpectrum SNR scan for targeted trainingSpectrum
soup canPack, inspect, verify, fork and run shareable .can artifactsSoup Cans
soup llamaProxy to the llama.cpp binariesGGUF and Ollama
soup pluginsDiscover, list, enable and disable pluginsPlugins
soup agentAgent Forge: spec to tool-calling dataset, train, evalAgent Forge
soup tokenizerTrain a BPE tokenizer from a JSONLv0.53.9 notes
soup advisePre-flight decision: prompt engineering, RAG, SFT, DPO or GRPOsoup advise
soup shipSHIP or DON'T SHIP verdict: task win and no catastrophic forgettingsoup ship
soup loopData flywheel: traces to preference pairs to DPO to gate to canarysoup loop
soup bomCycloneDX and SPDX bill of materialsGovernance
soup attestIn-toto and SLSA-3 attestations, ed25519 or Sigstore signedGovernance, Supply chain security
soup audit-logTail and rotate the local JSONL audit logGovernance
soup editKnowledge editing with ROME, MEMIT or AlphaEditUnlearning and editing
soup steerActivation steering with CAA, ITI or RepERAG and steering
soup envEnvironment lockfile and ABI drift detectionPre-flight and tooling
soup probeActivation probes: sleeper, truth, harm, interference, SAE diff, probe packPost-train x-rays
soup lockShared run lockfile: write, show, checkAdapter lifecycle
soup local-rlPersonal-LLM feedback flywheel: init, status, record, harvest, trainAnti-trend insurance
soup iterative-dpoSample, score, pair and retrain loopLoop hardening
soup ra-ditRA-DIT two-stage retriever-then-generator runRAG and steering
soup mcpModel Context Protocol server for coding agentsMCP server
soup draftTrain and measure a speculative-decoding draft modelsoup draft
soup rewardSynthesise and stress-test a deterministic reward verifierReward Forge

Global Flags

bash
soup --verbose <command>            # Full tracebacks instead of friendly errors (short form -V)
soup --log-level debug <command>    # quiet | normal | verbose | debug (default: normal)
soup --no-audit-log <command>       # skip ~/.soup/audit.jsonl for this invocation (or SOUP_NO_AUDIT_LOG=1)
soup --no-telemetry <command>       # opt out for this invocation (telemetry is off unless SOUP_TELEMETRY=1 anyway)

Note: all four are root options: they must go before the command name, not after (soup train --no-telemetry is refused with No such option). soup --install-completion and soup --show-completion are the two shell-completion options.

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.