The scripts are in the repository, not in the installed package. Clone it,
then
cd recipes/04-train/hosted-loop before running the commands below. Browse this recipe on GitHub.wai.train returns and how to wait on it, what
wai.serve gives you back, and how to call the served adapter. The rows
are deliberately small; this is the wiring check, not a result. You need
WHILEAI_API_KEY (or wai login); no model key, since the rows
come from the template writer and a scripted agent.
What each step does
One run of
python run.py with the defaults (--seed 1 --budget 96,
SFT on Qwen/Qwen3-4B, one epoch): 24 train rows over 7 tasks, and a
72-row holdout because the splitter seeds the held-out side by failure
signature first (see below). The loss numbers are one run’s; the
platform trainer is not seeded, so yours will differ:
== call is the same kind
of wiring check: it shows the endpoint answers, and yours will differ. Replace scripted_agent and judge with
yours, or point data at rows you already graded.
What to know before you run it
- Only two bases serve.
Qwen/Qwen3-4Bandmicrosoft/phi-4. The trainer’s defaults (Qwen2.5-0.5B for SFT, 1.5B for GRPO and DPO) train faster but cannot be hosted;wai.trainwarns andwai.serverefuses. SFT runs on an A10G and takes about a minute here; GRPO and DPO run on an L40S (--method grpo --steps 10took 137 s on Qwen3-4B). - Cold starts. The serving GPU scales to zero. The first call after
idle can take a few minutes;
callwaits up to fifteen, and a 502, 503 or 504 while the container is still waking is retried inside that window rather than raised. - Thinking mode. Qwen3 reasons before it answers unless told not to.
callsendschat_template_kwargs: {"enable_thinking": false}so the reply is the answer, not the reasoning. - Cost. SFT here is about a minute of A10G, GRPO a few minutes of L40S, and
run.training["cost_usd"]says what that came to: an estimate at Modal’s list price (cost_basisnames the rate and the day,estimate: A10G at $1.10/h, modal.com/pricing 2026-09-20), so a run this size is a few cents;print(run)shows it asabout $0.02 (A10G, 56 s, estimate). Serving bills while the GPU is awake; the endpoint idles back to zero on its own, and rollouts and judge calls are not priced. - Holdout.
split_pseudo_productionmoves whole tasks and seeds the held-out side with one task per failure signature first, so on a tiny set (7 tasks here) the holdout ends up larger than the fraction asks. That is fine for a wiring check; a real set has hundreds of tasks.
Where it shows up
run.url is the loss curve and the before/after on the platform.
wai.models() lists what the account hosts, wai.get_run(run_id) returns
the points, and the dataset cards link to the run. Docs:
https://docs.while.ai/api/training