fix(textarena_env): forward reset(seed=...) so episodes are reproducible - #1127
fix(textarena_env): forward reset(seed=...) so episodes are reproducible#1127akashjainn wants to merge 2 commits into
Conversation
TextArenaEnvironment.reset() accepts a seed, as every Environment does, and called self._ta_env.reset(num_players=...) without it. The seed had no effect and nothing reported that, so a caller reading the signature would reasonably believe an episode was reproducible when it was not. Six resets at seed=1234 on Wordle-v0 gave six different secret words (clock, sugar, month, price, flame, steam). With the seed forwarded they give "slope" every time, and a fresh environment at the same seed gives it too. TextArena's own reset takes seed on Env and on Wrapper, ObservationWrapper, ActionWrapper and RenderWrapper alike, so forwarding is safe through the wrapper chain OpenEnv builds. Checked determinism on Wordle, GuessTheNumber, Hangman, Crosswords, Sudoku and FifteenPuzzle: unseeded resets vary, a fixed seed is stable, and different seeds differ. __init__ still resets unseeded. It only exists to leave the env in a valid state before the first step(), and reset() replaces that state, so threading a seed through the constructor would widen the change without changing behaviour. Fixes huggingface#1102
|
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.
Forwarding fixes single-session reproducibility, but the pinned TextArena 0.7.4 implementation calls process-global random.seed(seed) during reset. Because this wrapper advertises concurrent sessions, the one-line change leaks seeded state into other sessions and can race. Please either incorporate a serialized save/seed/reset/restore implementation with non-skipped TextArena coverage, or supersede this PR with the corrected preservation approach in #1078.
Sent by Cursor Automation: Release
| env.full_observations = {} | ||
|
|
||
| self._ta_env.reset(num_players=self.num_players) | ||
| self._ta_env.reset(num_players=self.num_players, seed=seed) |
There was a problem hiding this comment.
This call mutates process-global RNG state in TextArena 0.7.4 (SinglePlayerState.__init__ → State.__init__ → random.seed(seed)). A concurrent or subsequent unseeded session therefore observes the seeded stream. Serialize all underlying resets with a process-wide lock and save/restore RNG state for seeded calls; add coverage that proves seeded resets neither perturb nor race an unseeded session.


Summary
TextArenaEnvironment.reset()accepted aseedand dropped it, so episodes were never reproducible even though the signature said they were. This forwards it to TextArena's ownreset, which has always taken one.Fixes #1102.
Type of Change
Alignment Checklist
.claude/docs/PRINCIPLES.mdand this PR aligns with our principles.claude/docs/INVARIANTS.mdand no invariants are violatedbash .claude/hooks/lint.shand tests and addressed all issuesReproducibility is something the project already optimises for, and the Gymnasium-style
reset/step/stateAPI is a stated principle. Aseedparameter that is accepted and ignored works against both.RFC Status
Test Plan
Before, on
Wordle-v0, reading the secret word off TextArena's own game state:After:
slopematches what the reporter independently got when they tried forwarding the seed.Three tests added to
tests/envs/test_textarena_environment.py:The first two fail without the change; the third passes either way and is there to catch an over-broad fix.
Notes
The issue asked whether forwarding is correct for every wrapped TextArena game, or whether raising on a non-
Noneseed would be safer.seed: Optional[int] = Noneis onEnv.resetand onWrapper,ObservationWrapper,ActionWrapperandRenderWrapper, so it is safe through the whole chain, and determinism holds on Wordle, GuessTheNumber, Hangman, Crosswords, Sudoku and FifteenPuzzle. So forwarding rather than raising.__init__still resets unseeded. That call only exists to leave the environment valid before the firststep(), andreset()replaces the state it creates, so seeding it would mean widening the constructor signature for no behaviour change. Happy to do it if you would rather the constructor took a seed.@caiotheodoro offered to open a PR for this in the issue. I had it written before picking the issue up and thought the multi-game determinism check was worth having either way, but I am glad to close this if they would rather carry it.
Note
Low Risk
Single-line behavior fix aligning with the documented Gymnasium-style API; tests cover seeded vs unseeded reset behavior.
Overview
TextArenaEnvironment.reset()now forwardsseedto TextArena’s underlyingreset, so callers get reproducible episodes instead of acceptingseedand ignoring it.New Wordle-focused tests assert the same seed yields the same puzzle (via
game_state["secret_word"], not the static prompt), reproducibility across fresh env instances, and that resets without a seed still vary.Reviewed by Cursor Bugbot for commit 5586da8. Bugbot is set up for automated code reviews on this repo. Configure here.