import whileai.simulations as wai, then wai.name.
export_dataset
training_rows as JSONL. Never overwrites the source.
export_dataset is this same function object under the product
name — export_dataset is export_training — not a second exporter.
There is no behavioral difference to pick between: same arguments,
same file, same report. export_dataset is the name to write in
new code (it exports a dataset, not a training run); export_training
is the older spelling and is kept so nothing written today breaks.
With a path source and no output, writes <name>.train.jsonl
next to it. validate=True refuses to write a dataset whose tool
calls do not round-trip to structured arguments, or whose assistant
turns quote the row’s own privileged block (the export scrubs the
key, not the reply that recited it); pass validate=False to export
anyway and read the report instead. The leak check reads the source
before the scrub, so pass the SimulationData or its
trajectories; rows that already came through rows(), save()
or a file carry nothing to check, and report["privileged_leaks"]
says so.
format="openai" (the default) writes the OpenAI chat-completions
wire row: the full messages list, function.arguments as a JSON
string, and the ask carried alongside as prompt.
format="trl" writes what trl can actually load: conversational
SFT rows (messages only, arguments as dicts, the ask under
prompt_text). TRL decides “is this conversational?” from the
column set, so a prompt string beside messages makes it skip
the chat template without an error and train on the bare ask — which
is why the TRL rows do not carry one. The report says which format
and which argument encoding the round-trip gate checked.
export_preference
format="openai" (the default) each line is \{"prompt": "<the ask, as text>", "chosen": [...messages...], "rejected": [...messages...]\} in the same wire format as export_dataset:
both sides are the whole conversation, prompt turns included, and
tool-call arguments are JSON strings.
With format="trl" each line is TRL’s conversational preference
triple: prompt is the message list up to the first assistant turn
and chosen/rejected are the completions only, with
arguments as dicts. The default shape is not loadable by
trl.data_utils.maybe_apply_chat_template — a prompt string
with conversational sides raises TypeError: string indices must be integers — so pass format="trl" when a TRL trainer is the
consumer. Pairs whose chosen or rejected side has no completion after
the prompt prefix are dropped (no_completion_dropped).
The roundtrip gate runs over BOTH sides and names the encoding it
checked. Pairs come from ScoredData.select_for_preference() /
build_preference_pairs. A pair judge_pairs marked tie
carries no preference and is left out (ties_dropped in the
report) unless drop_ties=False.
export_training
training_rows as JSONL. Never overwrites the source.
export_dataset is this same function object under the product
name — export_dataset is export_training — not a second exporter.
There is no behavioral difference to pick between: same arguments,
same file, same report. export_dataset is the name to write in
new code (it exports a dataset, not a training run); export_training
is the older spelling and is kept so nothing written today breaks.
With a path source and no output, writes <name>.train.jsonl
next to it. validate=True refuses to write a dataset whose tool
calls do not round-trip to structured arguments, or whose assistant
turns quote the row’s own privileged block (the export scrubs the
key, not the reply that recited it); pass validate=False to export
anyway and read the report instead. The leak check reads the source
before the scrub, so pass the SimulationData or its
trajectories; rows that already came through rows(), save()
or a file carry nothing to check, and report["privileged_leaks"]
says so.
format="openai" (the default) writes the OpenAI chat-completions
wire row: the full messages list, function.arguments as a JSON
string, and the ask carried alongside as prompt.
format="trl" writes what trl can actually load: conversational
SFT rows (messages only, arguments as dicts, the ask under
prompt_text). TRL decides “is this conversational?” from the
column set, so a prompt string beside messages makes it skip
the chat template without an error and train on the bare ask — which
is why the TRL rows do not carry one. The report says which format
and which argument encoding the round-trip gate checked.
loss_mask
"assistant" trains every assistant turn, the multi-turn default.
"final" trains only the last assistant turn, for conversations
whose earlier agent turns were scripted or came from another policy
(rlhf-book ch. 4 “Implementation Details” describes both). System,
user, and tool messages are always 0: tool output is the environment
speaking, not the policy, and training on it teaches the model to
invent tool results (ch. 13).
to_trl
training_rows / export_preference in TRL’s shape.
kind="training" reshapes SFT rows, kind="preference" DPO
pairs; see the module docstring for what each shape is and why it
differs from the default OpenAI wire rows. Equivalent to passing
format="trl" to the exporters, for callers that already hold
rows. Preference pairs whose chosen or rejected side has no
completion after the prompt prefix are dropped.
tool_call_roundtrip
format="openai"(encoding: "json_string"): arguments must parse back to a dict. Arguments that survive as un-parseable strings get re-quoted by chat templates and teach the model to emit string-wrapped arguments, which then spiral on tool rejections.format="trl"(encoding: "dict"): arguments must already be dicts. A JSON string here is valid OpenAI wire and still wrong for a chat template, which would render it quoted twice, so it counts as invalid rather than passing on a technicality.
training_rows
source is a SimulationData (system prompt and tools come from
its profile), a row list, or a JSONL path. For lists and paths, pass
system_prompt= and tools= explicitly; a row exported without
its policy trains an agent that never saw its rules. mask_mode
picks which assistant turns carry loss (see loss_mask).
strip_think=True (the default) removes <think> blocks from the
assistant turns. On a reasoning base such as Qwen3 that teaches the
adapter to emit an empty <think></think> and answer at once, so
at eval it answers while the untrained base is still reasoning under
the same max_tokens (#297). Pass strip_think=False when the
student should keep reasoning, and set thinking= the same on both
arms of the eval either way.
unroll=True turns an N-turn conversation into N samples, the
k-th ending at the k-th assistant turn with loss on that turn only
(rlhf-book ch. 4, multi-turn masking). Every earlier agent turn then
trains once with exactly the context it had, instead of only the
last one (mask_mode="final") or all of them at once ("assistant",
where later turns see context the policy never produced). Each sample
carries unroll (turn, turns) and lineage.unrolled_from
(the source row’s prompt hash and rollout index); mask_mode is
ignored, and no group fields are stamped, since samples of one
conversation are not a GRPO group.
max_tool_output_chars caps each tool message at that many
characters, appending [... N chars of tool output truncated] and
counting the cut on the row as tool_output_truncated (messages)
and tool_output_chars_cut. Tool output is masked from the loss
anyway; what it costs is context, and the cut is explicit rather than
silent (rlhf-book ch. 13). None cuts nothing.