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.

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

bash
pip install "soup-cli[tui]"
soup tui --refresh 1.0 --limit 50

soup train --profile

Records a torch.profiler Chrome-trace over an early-steps window (default wait=1, warmup=1, active=5, repeat=1).

bash
soup train --config soup.yaml --profile
# → ./output/profiles/<run_id>.trace.json

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

bash
soup runs show <run_id>
# Cost: $4.21  Duration: 1h 38m  GPU: RTX 4090

CPU / MPS / unknown GPUs render — (no fabricated zeros).

bash
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

bash
soup runs replay <run_id>

Replay summary + downsampled loss curve from history (no live training restart).

Global --log-level

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