simulate()
emits rows; so does wai.rows(), from prompts and completions you already
have (a public benchmark, a model’s answers to it). Every measurement call
reads the same five keys, so the rows from either source go into every call
unchanged.
The five keys
A row may carry more (
messages, steps, judge_name, lineage,
privileged) and the calls use what they find. Nothing else is required.
The typed view is whileai.simulations.schema (Task, Rollout,
Judgment, Marker); wai.rows() builds through it, so its rows carry
schema_version and the same shape a run writes.
Build them: wai.rows()
prompts: strings, or message lists ([{"role": "user", "content": ...}]); the row’spromptis the last user turn andmessageskeeps the list.completions: one string per prompt, or one sequence per prompt. The sequence is k completions of the same prompt, which is whatpass_at’s k-way numbers andselect(mode="rl")need.reward: a verifier (wai.verify.MathEqual(),Numeric,ExactMatch,CodeExec), a callable(prompt, completion)or(prompt, completion, reference)returning a number in [0, 1], a judge-contract callable(row) -> verdict, or the numbers themselves, nested likecompletionsor flat. A verifier or callable runs through the same pathdata.grade()uses, so the row says what scored it.references=: the gold per prompt. It lives underprivileged.reference, where a verifier reads it and no training export projects it.task_ids=,markers=: names per prompt, measurements per completion.
Then measure
Everything downstream takes the list as it is.select(mode="rl") keeps whole groups the model passes between 20% and
80% of the time (Lambert 2025, chapter Reasoning; DAPO, arXiv:2503.14476)
and drops the rest: a group all-pass or all-fail has no advantage to learn
from. Its report names every gate that dropped a row, so a benchmark whose
rows all vanish says why.
The worked example, 200 GSM8K test questions through MathEqual, three
eval passes, holdout_size and a compare report, offline, is
recipes/02-measure/public-benchmark.
What the calls read
Every call in this table is onwai, so the one import reaches all six.
The noise floor and the size of the set
A delta is a result against a floor, so two of those six calls run before and besidecompare. wai.holdout_size(effect) says how many paired
tasks can prove a gain of that size, before any GPU runs.
wai.eval_variance(run_1, run_2, run_3) evaluates the same model several
times and reports how far the number moves on its own. Both print
themselves.
wai.compare(run_std=, run_std_runs=) and a delta
inside the band reads as what re-running the eval does on its own, not as
a gain. Three re-runs is the fewest that give a standard deviation worth
reading; below that the report says so instead of printing a bare number.