build_loss contract.
Distillation objectives (OPD, OPSD, SDPO) have their own group — see
Distillation.
Shared Whitney loop
forward_backward, optim_step, save_weights_for_sampler, sample, logprobs.
Before allocating GPU work, confirm the capability row for your provider and
model advertises every operation you use. If the contract cannot be represented
in Whitney JSON primitives, stop before creating a session.
GET /v1/training/capabilities does not return a loss-function list — it
cannot, because Whitney forwards loss_function straight through to the
provider. See Provider loss support
for the caller-side table of what each provider actually accepts.
GRPO without standard deviation
Contract: group advantage without standard-deviation normalization. Caller math: After sampling, compute group-mean advantages only. Do not divide by per-group standard deviation. Whitney mapping: Same loop as GRPO. Encode the no-std objective inForwardRequest.loss_function and loss_config.
Fail closed: Do not use generic GRPO with std normalization.
Length-normalized GRPO
Contract: explicit token or sequence length-normalized loss. Caller math: Apply your documented length weighting when building the loss tensor inputs insideForwardRequest.
Whitney mapping: Standard GRPO loop; length normalization lives entirely in
caller-constructed loss_inputs and loss_config.
Fail closed: Whitney does not apply length normalization implicitly.
Dr.GRPO
Contract: no-std, length-normalized GRPO with no implicit KL penalty. Caller math: Combine no-std group advantages, explicit length weighting, and omit any implicit KL term your generic GRPO helper might add. Whitney mapping: Standard GRPO loop with a customForwardRequest. “No
implicit KL” needs no extra code — Whitney never adds one on its own.
Fail closed: Not interchangeable with generic GRPO or length-normalized GRPO
alone.
PPO
Contract: policy plus first-class critic/value network and GAE lifecycle. Caller math: Group-relative advantages, same as GRPO — no critic.ppo’s
policy loss reads (log_probs, old_log_probs, advantages, config, loss_mask, rollout_logprobs), the same four tensors as importance_sampling. The
critic-needing variant is the separately advertised ppo_critic, not plain
ppo.
Whitney mapping: ppo is a real loss name on both providers. The clip
range is set two different ways per provider, same key names:
- Tinker Cloud:
clip_low_threshold/clip_high_thresholdas per-token tensors (rl_datum(..., clip_low=, clip_high=)). - Modal: the same two names as scalar
loss_configentries — see Provider loss support.
require_advertised_loss(provider, "ppo") and
never send ppo_critic.
CISPO
Contract: provider-advertised CISPO importance-sampling loss and data layout. Caller math: Build standard GRPO-shaped data (group-mean advantages overrl_datum); the only difference from generic GRPO is loss_function.
Whitney mapping: cispo is in both providers’ advertised loss sets. On
Modal, clip_low_threshold/clip_high_threshold are valid scalar
loss_config keys (same mechanism as PPO, above); Tinker Cloud’s CISPO
loss_config contract isn’t published, so leave it empty there.
Fail closed: Never fall back to generic importance sampling or GRPO —
set loss_function explicitly.
GSPO
Contract: provider-advertised GSPO loss and data contract. Caller math: Same group-relative, no-critic shape as PPO/CISPO — GSPO’s “sequence-level importance ratios” are computed by the provider from the sameweights/logprobs tensors; nothing extra to build caller-side.
Whitney mapping: gspo is a real, Modal-only loss name — absent from
Tinker Cloud entirely. Its recommended sequence_mean reduction mode is
not a settable loss_config key (only clip_low_threshold/
clip_high_threshold are), so it runs on whatever Whitney’s deployed default
uses.
Fail closed: Gate to Modal only via require_advertised_loss. Not a
generic importance-sampling fallback.
DPPO
Contract: DPPO plus critic/value lifecycle. Caller math: No critic needed — DPPO’s policy loss replaces PPO’s ratio clipping with a divergence-based binary mask computed fromrollout_logprobs, reading the same tensor set as every other RL loss here.
Whitney mapping: dppo is a real, Modal-only loss name. Its divergence
variant (binary_tv vs binary_kl) is fixed by Whitney’s deployed default —
not a settable loss_config key; only the divergence thresholds
(delta_low/delta_high) are.
Fail closed: Gate to Modal only. Absent from Tinker Cloud entirely.
DAPO
Contract: dual clipping, dynamic rejection or resampling, overlong-sample filtering. Caller math: After sampling, filter overlong or invalid outputs, resample per your declared policy, compute advantages on the retained group, apply upper and lower clipping, then buildForwardRequest.
Whitney mapping: Multiple sample cycles may be needed per optimizer step.
build_loss receives a LossContext with a live client handle, so it can
issue additional sample operations itself, inside one cycle, rather than
needing a separate loop file. Each resample is still a real, billed, ordered
operation. Neither provider exposes a dedicated “dual-clip” loss name on the
wire — only regular/ppo-shaped clipping is selectable — so this
approximates DAPO with loss_function="ppo" plus a clip range, the same
per-provider mechanism as the PPO section above, not the paper’s asymmetric
dual clip.
Fail closed: Filtering and resampling semantics must be caller-visible; do
not pretend one generic sample batch is DAPO.
SAPO
Contract: provider-native SAPO loss with explicit tau parameters. Caller math: N/A — this is the one entry in the index that cannot be sent today, not a caller-side gap. Whitney mapping: Not selectable. The provider’s own Tinker-compatible API restricts the selectable loss to a fixed set that excludes SAPO — aloss_function="sapo" request is rejected by that validation before it
reaches any code that might support it internally.
Fail closed: Do not substitute a generic clipping loss for SAPO. Check
Provider loss support — or call
GET /v1/training/capabilities — before allocating.
RLOO
Contract: leave-one-out baseline plus reference or KL logprobs. Caller math: Compute leave-one-out baselines across grouped samples. Fetch reference logprobs vialogprobs when KL is part of the objective.
Whitney mapping: sample → logprobs (reference) → forward_backward with
RLOO-encoded loss.
Fail closed: Group-mean GRPO is not a valid RLOO substitute.
The leave-one-out baseline only; an optional reference-KL term follows the
same pattern as REINFORCE++ below via a frozen second run.
REINFORCE++
Contract: REINFORCE++ with K2 and reference-KL semantics. Caller math: Implement K2 baseline and reference-KL terms in caller code. Uselogprobs for reference policy probabilities.
Whitney mapping: A single run’s sampler only ever serves its current
weights, so a genuine frozen reference needs a
second Whitney run, synced once and never trained, that
supplies its logprobs on the student’s own generated tokens to build_loss
every cycle.
Fail closed: Requires advertised K2/reference-KL semantics.
The K2 KL estimator, 0.5 * (student_logprob - reference_logprob) ** 2, is
one reasonable reading of “K2 semantics”, not a literature citation:
MaxRL
Contract: explicit MaxRL estimator. Caller math: Encode the MaxRL objective directly inForwardRequest. No
provider configuration name substitutes for this contract.
Whitney mapping: Standard RL operations; estimator logic is entirely caller-owned.
Fail closed: Do not map MaxRL to generic GRPO.
No canonical MaxRL formula exists; this is one reasonable, clearly-editable
choice (a leave-one-out “beat the group’s best” estimator), not a spec:
Implementing with a coding agent
Point an agent at this page and GRPO. Ask it to:- Read capabilities for the target model.
- Start from the matching section here (or Distillation for
OPD/OPSD/SDPO) and the shared loop building blocks —
most entries already have a real
build_losssnippet to extend rather than re-deriving the loop from prose. - Never import a provider SDK or substitute generic GRPO when the contract forbids it.
