Wrappers
Wrapper Synopsis
Wrappers compose around environments via nesting: Wrapper2(env=Wrapper1(env=base_env)).
Each wrapper may transform observations, actions, or spaces, and may add its own fields
to the state or info.
Wrappers that need to track data across steps (e.g. a step counter, running statistics)
extend WrappedState, which nests the inner environment's state as inner_state.
Wrappers that only transform observations or actions (like ClipActionWrapper or
ContinuousObservationWrapper) pass state through without wrapping. The unwrapped
property traverses the full nesting to return the base environment's state.
Wrappers communicate additional data to user code by adding fields to the info via
info.update(...). For example, EpisodeStatisticsWrapper adds stats,
AutoResetWrapper adds final (a snapshot of the terminal step's info, enabling value
bootstrapping), and ObservationNormalizationWrapper adds unnormalized_obs.
Vectorization
Three wrappers add batch dimensions:
VmapWrappervmaps a single environment withbatch_sizeparallel instances.VmapEnvsWrappervmaps over a batched pytree of environment instances, for example created viajax.vmap(make_env)(params). This is useful when different instances have different configurations.PooledInitVmapWrappervmaps likeVmapWrapper, but pre-computes a pool of initial states and samples from them on reset. It also includes built-in autoreset logic, making it an alternative toAutoResetWrapper+VmapWrapper.
Wrapper Ordering
The key constraint is that AutoResetWrapper calls reset() on its inner wrapper chain
when an episode ends. Wrappers that need their reset() triggered on episode boundaries
(e.g. TruncationWrapper resetting its step counter) must therefore be inside
AutoResetWrapper. Vectorization wrappers must be outside, since autoreset operates
per-element.
From innermost to outermost:
base env → Observation/action transforms → Episode logic → AutoReset → Vectorization
A concrete example with all layers:
VmapWrapper # outermost: adds batch dim
└─ AutoResetWrapper # resets on done, adds `final`
└─ StateInjectionWrapper # (optional) overrides reset target
└─ EpisodeStatisticsWrapper # tracks reward/length
└─ TruncationWrapper # caps episode length
└─ ObservationNormalizationWrapper
└─ ContinuousObservationWrapper
└─ ClipActionWrapper
└─ base env # innermost
Not all wrappers are needed in every pipeline. The ordering between wrappers in the same layer (e.g. the observation/action transforms) is flexible.
API Reference
envelope.wrappers.Wrapper
envelope.wrappers.WrappedState
Bases: FrozenPyTreeNode
envelope.wrappers.AutoResetWrapper
Bases: Wrapper
Wrapper that automatically resets the environment when an episode ends.
When a step results in termination or truncation, this wrapper immediately resets the environment. The returned info preserves critical information from the terminal step while providing the new episode's initial observation.
Info fields after a terminal step (terminated=True or truncated=True): obs: Initial observation from the new episode (after reset). final: Full info snapshot from the terminal step (before reset). terminated: True if the episode ended due to termination. truncated: True if the episode ended due to truncation. reward: Reward from the terminal step.
Info fields during normal steps (terminated=False and truncated=False): obs: Current observation. final: Info snapshot from the last completed episode (persisted). terminated: False. truncated: False. reward: Reward from the step.
This design enables correct value bootstrapping
- Use final.obs for value estimation of the true next state
- On termination: V(s_final) = 0 (episode truly ended)
- On truncation: bootstrap from V(final.obs) (episode cut off artificially)
- final persists until the next episode completes, giving easy access to last episode's aggregated stats (e.g., final.episode_return)
envelope.wrappers.ClipActionWrapper
Bases: Wrapper
envelope.wrappers.ContinuousObservationWrapper
Bases: Wrapper
envelope.wrappers.EpisodeStatisticsWrapper
Bases: Wrapper
envelope.wrappers.FlattenActionWrapper
Bases: Wrapper
envelope.wrappers.FlattenObservationWrapper
Bases: Wrapper
envelope.wrappers.ObservationNormalizationWrapper
Bases: Wrapper
Attributes
stats_spec = static_field(default=None)
class-attribute
instance-attribute
Per-leaf normalization statistics spec as a pytree of jax.ShapeDtypeStruct. Shapes must be broadcastable to the observation leaves. If None, inferred from the observation_space with BatchedSpace ignored; each leaf must have a floating dtype.
envelope.wrappers.PooledInitVmapWrapper
Bases: Wrapper
envelope.wrappers.StateInjectionWrapper
Bases: Wrapper
Stores a state that all resets return to.
For UED: use set_reset_state() to update the injected state, then all resets (including auto-reset) return to that state until it's changed again.
Usage
env = AutoResetWrapper(StateInjectionWrapper(env=base_env))
state, info = env.init(key)
for outer_iter in range(num_outer_iters):
# Sample a new task and set it as the reset state
task_state, task_obs = sample_task(key)
state = env.set_reset_state(state, task_state, task_obs)
# Run episode - auto-resets return to task_state
for inner_step in range(num_inner_steps):
state, info = env.step(state, policy(info.obs))
Functions
set_reset_state(state, reset_state, reset_obs)
Update the state that resets will return to.
This method traverses the wrapped state hierarchy to find and update the InjectedState, then reconstructs the full state tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
WrappedState
|
Current state (can be from any outer wrapper) |
required |
reset_state
|
PyTree
|
The state to reset to (inner environment state) |
required |
reset_obs
|
PyTree
|
The observation to return on reset |
required |
Returns:
| Type | Description |
|---|---|
WrappedState
|
New state with updated reset fields at the appropriate level |
envelope.wrappers.TruncationWrapper
Bases: Wrapper
envelope.wrappers.VmapWrapper
envelope.wrappers.VmapEnvsWrapper
Bases: Wrapper
Vectorizes over a batched collection of environment instances (vmapping over 'self').
Usage
envs = jax.vmap(make_env)(params_batch) # env pytree batched on leading axis
wrapped = VmapEnvsWrapper(env=envs, batch_size=B)
state, info = wrapped.init(keys) # keys shape (B, 2) or single key
next_state, info = wrapped.step(state, action)