Repository navigation
Environment-owned dataset: make train_dataset optional - #6349
Conversation
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6edf17a2a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Approving the direction from the Harbor / OpenReward integration side — this is the right move, and the backward-compatible path (an external dataset still drives length; prompt just becomes optional) drops into both specs without breaking them. Filed #6354 to adopt environment-owned reward + dataset there on the back of this.
Two non-blocking follow-ups from that angle:
1. Self-sampling + num_generations > 1 quietly degrades the GRPO baseline. You flag this in the group-state note, but it's a real footgun: with an external dataset the G rollouts of a group share one task, so advantages are computed over the same initial state; with env-owned self-sampling each reset() can draw a different task, so the group's baseline is taken across different problems. Harbor/OpenReward sidestep it by staying dataset-driven (one task per row → shared across the group), which this PR preserves. For the new path, a warn when there's no dataset and num_generations > 1 (until group_id lands) would catch it — it won't show up in the loss curve.
2. No-dataset + dict environment_factory (#6002) don't compose. The placeholder is {"prompt": ...} with no environment column, so with a routing dict the first batch hits raise ValueError("Example has environment=None ...") — confusing, since the user passed no examples. Since env-owned data implies a single owning env, worth rejecting a dict factory upfront in the no-dataset branch (or stating single-factory-only in the docs).
Neither blocks — great ergonomics win for procedural / self-sampling envs.
|
Thanks! On the two follow-ups:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ea7ed9e. Configure here.
| prompt = row["prompt"] | ||
| # `prompt` is optional only when an environment owns the data; `reset()` then supplies it. Without | ||
| # an environment, a missing `prompt` is a malformed dataset and must still fail fast (KeyError). | ||
| if "prompt" not in row and self.environment_factories is not None: |
There was a problem hiding this comment.
Is this possible ? because we set placeholder dataset :
train_dataset = Dataset.from_dict({"prompt": [[{"role": "user", "content": ""}]] * num_placeholder_rows})There was a problem hiding this comment.
Yes; the placeholder isn't what this branch is for. It covers "no dataset at all", and those rows always have a prompt. This handles the other case: a user-provided dataset + environment_factory with no prompt column, e.g. a routing dataset carrying only environment. The environment_factories is not None half keeps a genuinely malformed dataset failing fast. Same shape as GRPOTrainer after 6013034.
| "`environment` column to route each example to its environment. Provide a dataset, or pass a " | ||
| "single environment factory." | ||
| ) | ||
| if self.args.max_steps <= 0: |
There was a problem hiding this comment.
right, they compose: #6385 says an explicit max_steps > 0 disables the epoch callback, and the env-owned path mandates max_steps > 0. That's this guard. So the callback never engages here.
Good thing too: #6385's target is ceil(num_train_epochs × |dataset|), and our |dataset| is just placeholder rows sized to one generation round, so that target would be meaningless. #6385 should add a test pinning "no dataset ⇒ epoch callback inactive".
# Conflicts: # trl/experimental/async_grpo/async_grpo_trainer.py # trl/trainer/grpo_trainer.py
# Conflicts: # trl/trainer/grpo_trainer.py

Closes #5903.
What
When an
environment_factoryis provided, the environment now owns the data.train_datasetbecomes optional:reset()returns the prompt.This removes the need to fabricate a dummy dataset just to drive the loop (procedural envs like chess, self-sampling envs like Wordle).
Applies to both
GRPOTrainerandAsyncGRPOTrainer.Behavior
train_dataset+environment_factory→ a placeholder dataset of empty prompts is synthesized internallytrain_dataset+ noenvironment_factory→ raises (unchanged intent)."prompt"column: any missing prompt defaults to an empty message thatreset()supplies. This makes multi-environment routing datasets that carry only anenvironmentcolumn work (the env owns the prompt).train_datasetstill works exactly as before: its rows feedreset(**kwargs)and its length drives the loop.Group-state note
Because the environment self-samples on each
reset(), theGmembers of a group may not share the same initial state (slightly noisier baseline). Passing agroup_idtoreset()for deterministic shared state is left to a follow-up.Note
Medium Risk
Changes core GRPO/AsyncGRPO data and prompt wiring; behavior is guarded and tested, but env-owned self-sampling can make group baselines noisier when group members get different
reset()states.Overview
train_datasetis optional forGRPOTrainerandAsyncGRPOTrainerwhen a singleenvironment_factoryis set: the environment supplies tasks viareset()(return string = user prompt), andmax_stepsdefines how long training runs. The trainer synthesizes a small placeholder dataset of empty prompts to drive the dataloader.Validation: no dataset and no environment still errors; dict multi-env factories still require a routing dataset with an
environmentcolumn; env-owned training without positivemax_stepserrors.Rollout path: with environments, missing
prompton a row defaults to an empty user message beforereset()observation is applied—so routing datasets can beenvironment-only. Without an environment, missingpromptstill fails.Reward models: conversational detection for nn.Module reward funcs now uses
is_conversational({"prompt": prompts[0]})in GRPO, RLOO, and SDPO (fixes prompts-only batches).Docs reframe env-first training, optional external datasets, and multi-env routing examples; tests cover guards and env-owned training without a zen prompt dataset.
Reviewed by Cursor Bugbot for commit 9cc8d5c. Bugbot is set up for automated code reviews on this repo. Configure here.