whileai/simulations/defaults.py with their
sources; equation numbers are the papers’. Each method’s own page says
where it runs: the hosted trainer (wai.train), prime-rl
(wai.prime_rl_config), or your own loop (method.update).
Notation
A batch holds trajectories. Trajectory answers prompt with reward and action tokens , each with history (the prompt and the tokens before it). Two policies can see every token: , the policy that wrote it (a row’sbehavior_logprobs; the rollout engine, or last week’s served model), and
, the one being trained (logprobs). Their per-token ratio is
and is a frozen reference (the starting weights) where a
method keeps one. A token the environment wrote (tool output, an
observation) has action_mask false: it is not one of the , gets
, and the critic methods bootstrap over it. When behavior_logprobs
is absent the row is taken as on-policy, .
The hosted four: wai.train(method=)
SFT. Maximum likelihood on the rows as pushed, over the assistant
tokens the loss mask keeps (the trainer does not filter on reward, so
push scored.passes()):
GRPO (Shao et al. 2024, arXiv:2402.03300). = generations
rollouts of the same prompt form a group; the advantage is the reward
centered on its siblings, and the coefficient is PPO’s clipped surrogate
with a KL to the reference:
A group whose rollouts all pass or all fail has for every
member, which is why select(mode="rl") drops unanimous groups before
the GPU is spent. loss_type picks , the length weight:
grpo averages each sequence’s tokens then averages sequences
(); bnpo pools every token in the batch ();
dr_grpo (Liu et al. 2025, arXiv:2503.20783) drops the standard deviation
from and uses one constant length (), removing the
bias toward long wrong answers. truncated="mask" gives a reply the token
cap cut ; "zero" scores it instead. Defaults: = 8,
= 0.2, = 0 (DAPO, Dr. GRPO and CISPO all run without
the KL term).
DPO (Rafailov et al. 2023, arXiv:2305.18290). A pass and a fail
of the same prompt, length matched; no sampling, no reward model:
RM. The same pairs train a scalar head under the
Bradley-Terry model; reward_model(run) is then a judge:
Distillation: wai.OPD, wai.OPSD
OPD, on-policy distillation (Agarwal et al. 2023, arXiv:2306.13649;
Thinking Machines 2025). The student samples its own reply; a frozen
teacher scores every token of it. The per-token loss is the
reverse KL from the student to the teacher over the teacher’s top-
tokens (Li et al. 2026, arXiv:2604.13016; Fu et al. 2026, arXiv:2603.25562),
and its sampled-token form is a policy gradient whose advantage is the
log-probability gap:
No reward, no group, no discount: a token the teacher liked more than the
student did is pushed up by the gap. divergence offers forward_kl and
jsd for a trainer that has them. Defaults: = top_k = 32,
samples = 4, temperature 1.0 on both sides so the teacher scores the
distribution the student drew from.
OPSD, on-policy self-distillation (SDFT, Shenfeld et al. 2026,
arXiv:2601.19897; SDPO, Hübotter et al. 2026, arXiv:2601.20802). The same
loss, with the teacher the student’s own weights shown a hint the
student never sees (a passing demonstration, the reference answer, a hint,
or a rollout plus the environment’s feedback, privileged):
The anchor is what keeps the teacher from chasing the student:
"ema:0.01" is the moving average above, "initial" freezes the starting
weights (Zhao et al. 2026, arXiv:2601.18734, with forward_kl), "live"
is the unanchored student prime-rl runs. samples = 1: there is no group
to form.
Staleness: wai.Async
Any method above, trained on rollouts that lag the policy by at most
off_policy_steps = optimizer steps, with one per-token correction
for the sampler/trainer gap that exists even at zero lag (Yao et al. 2025):
ipo masks on probability moved (prime-rl’s default), icepop masks
outside a ratio band (Ring-1T, arXiv:2510.18855), tis caps the ratio
(verl). Default = 8 (ScaleRL, arXiv:2510.13786; one step is free,
Noukhovitch et al. 2024, arXiv:2410.18252).
The group baselines prime-rl runs: "grpo", "max_rl", "rae"
Three string methods prime_rl_config writes, differing only in :
max_rl (arXiv:2602.02710) divides by the group mean rather than its
standard deviation, which makes the gradient unbiased for the
maximum-likelihood objective and weights a rarely solved prompt by about
; a group whose mean is 0 carries nothing. rae (SPIRAL,
arXiv:2506.24119) is a per-agent running average, the one baseline that
stands at one rollout per prompt, and on a single-agent taskset it is
REINFORCE against that average.
One rollout per prompt: wai.FlashReinforce, wai.SAO, wai.BPCO
The shape a production trace arrives in. With one rollout per prompt every
group above is the rollout itself, , and the update is
empty. Each of these three replaces the group with something a single
trajectory can supply, and each one’s update(batch) returns the
below as coefficients.
FlashReinforce
Baseline from the batch, a gate on drift, equal weight per trajectory. is the KL between two Bernoullis, “this token versus everything else,” so it needs only the one probability the sampler already stored. A trajectory over the gate drops out whole; the ratio is never clipped, only kept finite. Defaults: =trust = 0.003, a lag of
off_policy_steps = 8 updates.
SAO
A critic in place of the group, credit passed back over tool output, and a band that masks a drifted token instead of clipping it. Over the action tokens of one trajectory, in order, with on the last one and before it, the critic’s value at that token and past the end: The bootstrap is the next action token, so an observation between two actions is skipped, not scored. grows with length so the terminal reward reaches the first token with weight whatever is. No normalization: the critic is the baseline. The critic trains toward the Monte Carlo return, at every action token, with a squared-error loss,critic_steps
= 2 updates per policy update after critic_warmup = 10 critic-only
steps. Defaults: = gae_alpha = 1.5, = 1, the band
ratio = = (0.7, 6.0).
BPCO
A critic that cannot leave the reward’s range, trained on the reward the rollout earned, an advantage left at its natural scale, and a clip on probability rather than on ratio. The clip range is : a token the old policy gave probability 0.05 may move its ratio four times further than one it gave 0.2, because the same ratio change moves less probability mass. The loss sums a sequence’s tokens and averages over sequences, so no length weight enters . The critic trains alone forcritic_warmup = 15 updates first.
Defaults: = clip = 0.2, = gae_alpha = 0.4,
reward_range = = (0, 1).
Adaptive sampling: wai.methods.ReinforceAda
Reinforce-Ada (Xiong et al. 2025, arXiv:2510.04996) changes which
rollouts a group-relative update sees, not the loss. With a binary reward
and a group of , a prompt the policy solves with probability comes
back unanimous, and so with for every rollout, with probability
The paper’s reason to care is the objective. Maximizing
for a concave gives
so under the prompts the policy rarely solves weigh the most,
the same that max_rl puts in the advantage. Reinforce-Ada puts it
in the sampling budget instead. Each round draws = round_size
rollouts for every prompt still active, and a prompt retires once its
pool holds right and wrong
(exit="balanced") or one right (exit="positive"), for at most
= round_size max_rounds draws. From the
drawn for prompt it keeps = keep, balanced where the pool
allows, and sets
with = threshold. The baseline is the whole pool’s pass rate,
not the kept group’s mean: the kept group is balanced on purpose, so its
mean is whatever the prompt’s difficulty. There is no division by a
standard deviation. The coefficient is then GRPO’s, and the backward pass
is the same rollouts per prompt it always was; the cost is generation,
draws per prompt instead of .
wai.methods.ReinforceAda().trainer(GRPOTrainer) is a
GRPOTrainer whose generation step is this sampler (trl 0.19, with
num_generations = keep, num_iterations = 1, beta = 0). The
replication is
recipes/papers/reinforce-ada:
on GSM8K with Qwen2.5-1.5B it cut the prompts with no gradient from
0.52-0.62 to 0.25-0.33 of each batch at 2.5 times the GPU minutes, and
moved pass@1 by +0.047 [-0.047, +0.140] across two training seeds per
arm, flat. The prompts it could not rescue were ones the model always
solves, where 32 draws find no wrong answer; exit="positive" does not
spend draws on those. Defaults: keep = 4, round_size = 8,
max_rounds = 4, threshold = 0.7, the authors’ own, each named in
whileai/reinforce_ada.py.
Context as a file: wai.methods.ContextFile
Context Language Models (arXiv:2609.37725) let the model manage its own
context. The context is a file the model rewrites, not a transcript that
only grows. An episode here is edits and one answer. At edit the
model sees the file and input , and replies with ;
is then gone. At the end it sees and the question. Training is
stepwise GRPO: every step of trajectory gets the outcome advantage
. The paper adds a success-gated efficiency advantage on the
edits (its Eq. 6):
is the group’s successes, and is the mean cost over .
is the trajectory’s prefix-reuse cost: each step pays for the prompt
tokens that miss the KV cache and for its reply (ContextFile.cost).
gate picks . "paper", the default, takes every success.
"complete" also requires that the last file hold the whole state the
task defines (env.complete), and "off" is plain stepwise GRPO. In
recipes/papers/context-lm,
four seeds an arm on KVLog with Qwen2.5-1.5B:
The paper’s rule adds +0.02 [+0.01, +0.03] over plain GRPO at 15% fewer
tokens.
"complete" was built against a shortcut that one seed of an
earlier two-seed run found (copy only the latest chunk). The shortcut did
not come back in twelve runs, and the gate cost 0.04 against the paper’s
rule.
clm.play(generate, tasks, env) runs episodes with any generator that
maps a list of chats and a token cap to Reply objects.
clm.report(episodes) prints the pass rate, tokens per trajectory, file
size, and the share right with an incomplete file. In TRL,
clm.trainer(GRPOTrainer) is a GRPOTrainer whose generation step plays
the episode and gives each step its credit (trl 0.19, env= on
construction, num_iterations = 1, beta = 0). Defaults: w_eff = 0.25,
from the paper’s BrowseComp-Plus run (the code default is 1.0);
file_tokens = 256; answer_tokens = 48. Each is named in
whileai/context_file.py.
Rollouts that refine each other: wai.methods.Swarm
Particle swarm optimization over rollouts (Kennedy and Eberhart 1995).
Round 0 is particles fresh samples. Each later round a particle is shown
its own best attempt with the fitness’s feedback and, by topology, a
neighbour’s best, and writes the next attempt: solo shows no neighbour,
ring the better of two ring neighbours, star the best in the swarm.
The model is the velocity update. It is a rollout rule, not a trainer: it
decides which samples get drawn, not how the policy moves.
recipes/01-simulate/swarm-rescue:
partial credit on tests predicted a pass zero times below three quarters
of the tests, a model judge ranked passes at chance, and where the fitness
was a real hill the swarm learned the shown tests instead of the task.
Swarm.calibration(rows) prints P(correct | fitness bucket) over any
graded rows; a fitness with no correct attempt below full credit is a
cliff, and a swarm has nothing to climb on it. Defaults: particles = 8,
rounds = 3, temperature = 1.0, max_tokens = 2048, each named and
justified in whileai/swarm.py.
From symbol to field
For the three single-rollout methods,update(batch) returns an Update:
For the rest, the symbols are the knobs: is
generations, is
beta, is the trainer’s clip, is loss_type, is
off_policy_steps, is the rate in anchor, and every one is a
named constant in defaults.py with the paper it came from. What the
single-rollout three did on a GPU, against their own ablations, is the
table in lesson 9
and the three recipes under papers.