Observability & Dev UX (v0.34.0)
Tools that explain why a run misbehaved instead of dumping a stack trace.
soup why
Heuristic explainer — reads the most recent (or named) run and surfaces plain-English diagnoses with concrete next steps.
soup why # most recent run
soup why run_2026_abc # specific run id (or prefix)Detects: NaN/Inf loss, plateau (≥30 steps with <0.5% change), divergence (loss > 3× initial), persistent high gradient norm, learning rate outside the typical [1e-6, 5e-3] band. Pure rule-based — no model calls.
soup tui
Full-screen Textual dashboard. Two-pane: run list (left) + selected-run detail (right). r refreshes, q quits.
pip install "soup-cli[tui]"
soup tui --refresh 1.0 --limit 50soup train --profile
Records a torch.profiler Chrome-trace over an early-steps window (default wait=1, warmup=1, active=5, repeat=1).
soup train --config soup.yaml --profile
# → ./output/profiles/<run_id>.trace.jsonOpen in chrome://tracing or Perfetto.
Stopping and checkpointing a run that is already going
Plan on losing everything since the last scheduled save. v0.44 shipped two helpers for this moment, a Ctrl+C graceful-save handler and a .checkpoint_now trigger-file watcher, but neither is wired into soup train at v0.75.2.
Ctrl+C is an ordinary KeyboardInterrupt. soup train installs no handler that writes a checkpoint on the way out: the interrupt reaches the CLI's top level, which prints Interrupted. and exits with code 130. Only the checkpoints written on the training.save_steps schedule (default 100) survive, and soup train --resume auto continues from the newest one.
There is no trigger file. Nothing polls a .checkpoint_now file in the output directory, so touching one does nothing. The two modules, soup_cli.utils.graceful_save and soup_cli.utils.checkpoint_trigger, exist with unit tests, but nothing in the trainer or the train command calls them, even though upstream's operations document still describes both as live. Lower save_steps if you need a tighter recovery point.
Crash bundles — .crash files
When training fails, Soup auto-writes a self-contained .crash JSON to ./.soup-crashes/crash_<utc>_<hex>.crash containing:
- Redacted error trace
- Classified failure kind (
oom/nan/cuda/dataloader/nccl/other) - GPU state at crash time
- Env summary
- Last-50 metric rows
- Recursively-redacted config (
hf_*/sk-*/Bearer ...tokens become<redacted>)
The output_dir is reduced to os.path.basename so $HOME doesn't leak. Bundle truncated to 1 MB.
Per-run cost
Every completed run stores an estimated cost ($ per run) computed from the captured GPU device name and duration.
soup runs show <run_id>
# Cost: $4.21 Duration: 1h 38m GPU: RTX 4090CPU / MPS / unknown GPUs render — (no fabricated zeros).
soup cost --config soup.yaml # estimate before training
soup cost --config soup.yaml --gpu H100 # specific GPU
soup cost --config soup.yaml --json # machine-readable--json prints a JSON array with one object per provider and GPU (provider, gpu, cost_per_hr, duration_hrs, total_cost) instead of the table. If the training data cannot be read, the estimate assumes 10,000 examples; the table prints a warning saying so and --json does not.
soup runs replay
soup runs replay <run_id>Replay summary + downsampled loss curve from history (no live training restart).
Global --log-level
soup --log-level verbose train --config soup.yaml
soup --log-level debug runs show <id>Tiers: quiet | normal | verbose | debug. Wires a Rich-formatted logger on the soup namespace; debug enables timestamps + module paths.
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.