Skip to content

Core

The environment base classes and their result types. See the Environments, Wrappers and Multi-agent guides.

Environments

Env

Bases: SeededRNG, ABC, Generic[ObsType, ActType]

Abstract base class for all sekai environments.

Subclasses must
  1. Set self.observation_space and self.action_space in __init__
  2. Implement reset() and step()

Unlike gym/gymnasium: - step() returns a StepResult dataclass, not a 5-tuple - reset() returns a ResetResult dataclass, not a 2-tuple - async_step() and async_reset() are available on all envs by default - render() is NOT part of this interface; use a Renderer object

If a subclass overrides step() (or reset()) without also overriding async_step() (or async_reset()), the async method is routed through the sync one. This keeps await env.async_step(a) from silently skipping logic that only lives in step(), e.g. in a custom wrapper. (A wrapper that only overrides step() therefore makes async calls through it synchronous; override async_step() too to keep an async-native env async end to end.)

unwrapped property

unwrapped: Env[ObsType, ActType]

The base environment underneath any wrappers.

reset abstractmethod

reset(*, seed: SeedType = None, options: dict[str, Any] | None = None) -> ResetResult[ObsType]

Reset the environment and return the initial observation.

Call self._set_rng(seed) first thing so seeding behaves consistently: an int seed re-seeds the env's RNG, None continues the current stream.

step abstractmethod

step(action: ActType) -> StepResult[ObsType]

Take a step in the environment.

async_reset async

async_reset(*, seed: SeedType = None, options: dict[str, Any] | None = None) -> ResetResult[ObsType]

async_step async

async_step(action: ActType) -> StepResult[ObsType]

close

close() -> None

Release any resources held by the environment.

MultiAgentEnv

Bases: SeededRNG, ABC, Generic[ObsType, ActType, AgentID]

Abstract base class for multi-agent environments.

Each agent gets its own typed observation and action space, supporting heterogeneous multi-agent setups (e.g. a portfolio manager agent + a risk manager agent with different obs/action spaces).

agents: currently active agents (may shrink as agents terminate) possible_agents: universe of all possible agent IDs

Seeding works as for Env: call self._set_rng(seed) at the start of reset() and draw randomness from self.rng.

num_agents property

num_agents: int

max_num_agents property

max_num_agents: int

reset abstractmethod

reset(*, seed: SeedType = None, options: dict[str, Any] | None = None) -> MAResetResult[ObsType, AgentID]

step abstractmethod

step(actions: dict[Any, ActType]) -> MAStepResult[ObsType, AgentID]

async_reset async

async_reset(*, seed: SeedType = None, options: dict[str, Any] | None = None) -> MAResetResult[ObsType, AgentID]

async_step async

async_step(actions: dict[Any, ActType]) -> MAStepResult[ObsType, AgentID]

observation_space

observation_space(agent: Any) -> Space[ObsType]

action_space

action_space(agent: Any) -> Space[ActType]

close

close() -> None

Release any resources held by the environment.

Wrappers

Wrapper

Wrapper(env: Env[ObsType, ActType])

Bases: Env[WObsType, WActType], Generic[WObsType, WActType, ObsType, ActType]

Transparent wrapper around an Env.

All method calls are delegated to the wrapped env by default. Override only the methods you need to change.

Improvements over gymnasium
  • __iter__ / __len__ let you inspect the full wrapper stack
  • Async methods are transparently delegated
  • _observation_space / _action_space overrides are lazy

If you override step() but not async_step(), async_step() calls your step(), so your logic is never bypassed. Override both to keep the inner env's async path end-to-end.

__iter__

__iter__() -> Iterator[Env[Any, Any]]

Iterate from outermost wrapper to base env.

__len__

__len__() -> int

ObservationWrapper

ObservationWrapper(env: Env[ObsType, ActType])

Bases: Wrapper[WObsType, ActType, ObsType, ActType], Generic[WObsType, ActType, ObsType]

Applies a transformation to every observation.

RewardWrapper

RewardWrapper(env: Env[ObsType, ActType])

Bases: Wrapper[ObsType, ActType, ObsType, ActType], Generic[ObsType, ActType]

Applies a transformation to every reward.

ActionWrapper

ActionWrapper(env: Env[ObsType, ActType])

Bases: Wrapper[ObsType, WActType, ObsType, ActType], Generic[ObsType, WActType, ActType]

Applies a transformation to every action before passing to the env.

Results

StepResult dataclass

StepResult(observation: ObsType, reward: float, terminated: bool, truncated: bool, info: InfoDict)

Bases: Generic[ObsType]

Structured result from Env.step().

Replaces the error-prone 5-tuple (obs, reward, terminated, truncated, info) pattern from gym/gymnasium.

ResetResult dataclass

ResetResult(observation: ObsType, info: InfoDict)

Bases: Generic[ObsType]

Structured result from Env.reset().

done is always False, so one loop handles both result types:

result = env.reset(seed=0)
while not result.done:
    result = env.step(policy(result.observation))

MAStepResult dataclass

MAStepResult(observations: dict[Any, ObsType], rewards: dict[Any, float], terminated: dict[Any, bool], truncated: dict[Any, bool], info: dict[Any, InfoDict])

Bases: Generic[ObsType, AgentID]

Structured result from MultiAgentEnv.step().

all_done / any_done look at every agent that appears in terminated or truncated, matched by agent ID; an agent missing from one of the dicts counts as not terminated (or not truncated) there. With no agents in either dict, both are False.

MAResetResult dataclass

MAResetResult(observations: dict[Any, ObsType], info: dict[Any, InfoDict])

Bases: Generic[ObsType, AgentID]

Structured result from MultiAgentEnv.reset(). all_done/any_done are always False.