> ## Documentation Index
> Fetch the complete documentation index at: https://docs.while.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install with `uv add whileai`; import as `import whileai as wai`.
> Run the offline path first (`simulator=False`, `wai.seeded_agent`, a callable judge); no key is needed for it.
> Report every pass rate with its interval and n, as `scored.pass_at` prints it.

# whileai.simulations.environment

> A verifiers environment for on-policy trainers.

3 public names. `import whileai.simulations as wai`, then `wai.name`.

| Name                                        | What it does                                                                 |
| ------------------------------------------- | ---------------------------------------------------------------------------- |
| [`build_tasks`](#build_tasks)               | One task per distinct prompt, split into train and holdout.                  |
| [`export_environment`](#export_environment) | Write graded rows as an installable RL environment for an on-policy trainer. |
| [`load_environment`](#load_environment)     | Build the verifiers environment from an exported `spec.json`.                |

### build\_tasks

```python theme={"theme":"vitesse-dark"}
build_tasks(
    rows: Sequence[dict],
    holdout: float | Sequence[str] = 0.2,
    band: tuple[float, float] | None = (0.2, 0.8),
    ngram: int = 8,
) -> tuple[list[dict], list[dict], dict[str, Any]]
```

Defined in [`whileai/simulations/environment.py`](https://github.com/whilehq/whileai-sdk/blob/main/whileai/simulations/environment.py).

One task per distinct prompt, split into train and holdout.

When a prompt has two or more graded rollouts its solve rate is known
(partial credit counts as it is) and, with `band`, prompts the policy
always or never solved are dropped: they carry no advantage
(Lambert 2025, chapter Reasoning, difficulty filtering at 20 to 80
percent; DAPO's dynamic sampling drops accuracy 0 and 1,
arXiv:2503.14476). Ungraded prompts and single
rollouts are kept as they are. `holdout` is a fraction, split by
scenario id (or the prompt) so a task is wholly on one side, or an
explicit list of holdout prompts. Train and holdout are decontaminated
against each other at `ngram`-grams (8: the overlap size
Lambert 2025, chapter Evaluation, found its contaminations with) and
the report says what overlapped.

### export\_environment

```python theme={"theme":"vitesse-dark"}
export_environment(
    source: Any,
    out: str | Path,
    name: str | None = None,
    reward: Any = None,
    execute: Any = None,
    system_prompt: str | None = None,
    tools: Sequence[dict] | None = None,
    holdout: float | Sequence[str] = 0.2,
    band: tuple[float, float] | None = (0.2, 0.8),
    max_turns: int | None = None,
    description: str = '',
    ngram: int = 8,
    world: Mapping[str, Any] | None = None,
    harnesses: Sequence[Any] | None = None,
    runtime: str = 'verifiers',
) -> dict[str, Any]
```

Defined in [`whileai/simulations/environment.py`](https://github.com/whilehq/whileai-sdk/blob/main/whileai/simulations/environment.py).

Write graded rows as an installable RL environment for an on-policy trainer.

Reach for it when the next step is RL in a trainer that speaks
verifiers (prime-rl and the like) and needs the tasks, the world and
the reward as one package. It writes the package under `out` and
returns the report, which is also the package README: `path`,
`prompts`, `tasks`, `train` and `holdout` counts,
`graded_prompts`, `band`, `band_dropped`, `graded_mixed`
(prompts the policy both solved and failed, the ones with an
advantage) and `decontamination`. `warnings` carries a line when
`graded_mixed` is 0, whether because no prompt has two graded
rollouts or because every graded prompt was unanimous: a grouped
update on such tasks has zero advantage everywhere (#684).

* `source`: a `SimulationData` (system prompt and tools come from
  its profile), a row list, or a JSONL path; graded rows get the
  difficulty band, ungraded rows are exported as they are. For a list
  or path pass `system_prompt` and `tools`.
* `reward`: a `Verifier`, a judge callable honoring the SDK judge
  contract, or `'module:attr'`; it must be importable in the trainer
  process. A `@verifier` or `All([...])` bound to a name in your
  own module is referenced by that name (a script run as `__main__`
  by its file stem, so keep that directory on the trainer's path).
  With no reward the conduct grade is used and the report warns: it
  is a process reward, and a policy trained on it alone learns to
  call nothing (see recipes/03-select/prime-intellect-rl).
* `execute`: a live world `(tool, arguments) -> result`; without
  it the SDK's mock world answers, seeded per task so every rollout
  of a task sees the same world.
* `world`: the mock world's dials as a dict (`WorldOptions`
  fields: `search_hits`, `exists_share`, `default_fault_mode`,
  name pools, ...). It is written into `spec.json` and the trainer's
  world is built from it, so the world a policy trains against is the
  one the export says.
* `holdout` (0.2): the share of tasks held out, split by scenario id
  (or the prompt) so a task is wholly on one side, or an explicit
  list of holdout prompts.
* `band` (`(0.2, 0.8)`): the pass-rate band a graded prompt must
  sit in; prompts the policy always or never solved carry no
  advantage and are dropped (Lambert 2025, chapter Reasoning, difficulty
  filtering at 20 to 80 percent; DAPO's dynamic sampling, arXiv:2503.14476).
  `None` keeps them all.
* `ngram` (8): the train-versus-holdout decontamination size, the
  overlap Lambert 2025, chapter Evaluation, found its contaminations with.
* `name`, `description`, `max_turns`: the package name, its
  README line, and the rollout turn cap (the SDK default when
  `None`).
* `harnesses`: `wai.Harness` objects (or plain `\{label,
  instructions, tools\}` dicts) the trainer's rollouts run under.
  Each task draws one from its id and a seed, and that harness's
  instructions become the system prompt and its tool schemas the tool
  set for the rollout; a harness with no tools of its own uses the
  environment's. The spec lists them as `\{label, hash, instructions,
  tools, disclosure\}` and every rollout records `harness = \{label,
  hash\}`. Kim et al. 2026 (arXiv:2606.25447): a policy trained under
  one fixed harness collapses when the tool environment shifts, and
  harness-aware post-training generalizes out of distribution. Left
  out, the spec is what it always was.
* `runtime` (`"verifiers"`): the trainer contract the package
  speaks. `"verifiers"` is the Prime Intellect package prime-rl and
  TRL's verifiers path install with `pip install -e`; `"openenv"`
  is Meta PyTorch's OpenEnv (`reset`/`step`/`state` over HTTP,
  the contract TRL, torchforge, SkyRL and Unsloth drive), written as
  an `openenv.yaml` package that `uv run --project . server`
  serves, `openenv build` containerizes and `openenv push` puts
  on a Hugging Face Space. Same tasks, world and reward either way;
  see `whileai.simulations.openenv`.

```python theme={"theme":"vitesse-dark"}
report = wai.export_environment(data, "envs/refunds", reward=my_verifier)
print(report["train"], report["holdout"], report["path"])
```

### load\_environment

```python theme={"theme":"vitesse-dark"}
load_environment(
    spec: str | Path | dict,
    split: str = 'train',
    reward: Any = None,
    execute: Any = None,
    world: Any = None,
    harness_mix: str | Sequence[float] = 'uniform',
    harness_seed: int = 0,
    **kwargs: Any,
) -> Any
```

Defined in [`whileai/simulations/environment.py`](https://github.com/whilehq/whileai-sdk/blob/main/whileai/simulations/environment.py).

Build the verifiers environment from an exported `spec.json`.

`split` picks the training task set; the holdout file, when present,
becomes `eval_dataset`. `reward` and `execute` override the
spec's references (a callable or `'module:attr'`). `world` (a
`WorldOptions` or a dict of its fields) overrides the mock world's
dials the spec carries; here callables such as `fault_modes` are fine.

When the spec carries `harnesses` (`export_environment(harnesses=)`),
each task draws one from a hash of `harness_seed` and its id, weighted
by `harness_mix` (`"uniform"`, or one weight per harness), and the
rollout runs with that harness's instructions as the system prompt and
its tool schemas as the tool set; the rollout state carries
`harness = {label, hash}`. Kim et al. 2026 (arXiv:2606.25447).
