class_docstr: An environment that interfaces with external agents.
  
  Unlike simulator envs, control is inverted: The environment queries the
  policy to obtain actions and in return logs observations and rewards for
  training. This is in contrast to gym.Env, where the algorithm drives the
  simulation through env.step() calls.
  
  You can use ExternalEnv as the backend for policy serving (by serving HTTP
  requests in the run loop), for ingesting offline logs data (by reading
  offline transitions in the run loop), or other custom use cases not easily
  expressed through gym.Env.
  
  ExternalEnv supports both on-policy actions (through self.get_action()),
  and off-policy actions (through self.log_action()).
  
  This env is thread-safe, but individual episodes must be executed serially.
  
  .. testcode::
      :skipif: True
  
      from ray.tune import register_env
      from ray.rllib.algorithms.dqn import DQN
      YourExternalEnv = ...
      register_env("my_env", lambda config: YourExternalEnv(config))
      algo = DQN(env="my_env")
      while True:
          print(algo.train())
class_docstr_tok: ["An","environment","that","interfaces","with","external","agents",".","Unlike","simulator","envs",",","control","is","inverted",":","The","environment","queries","the","policy","to","obtain","actions","and","in","return","logs","observations","and","rewards","for","training",".","This","is","in","contrast","to","gym.Env",",","where","the","algorithm","drives","the","simulation","through","env.step","(",")","calls",".","You","can","use","ExternalEnv","as","the","backend","for","policy","serving","(","by","serving","HTTP","requests","in","the","run","loop",")",",","for","ingesting","offline","logs","data","(","by","reading","offline","transitions","in","the","run","loop",")",",","or","other","custom","use","cases","not","easily","expressed","through","gym.Env",".","ExternalEnv","supports","both","on-policy","actions","(","through","self.get_action","(",")",")",",","and","off-policy","actions","(","through","self.log_action","(",")",")",".","This","env","is","thread-safe",",","but","individual","episodes","must","be","executed","serially",".","..","testcode",":",":",":","skipif",":","True","from","ray.tune","import","register_env","from","ray.rllib.algorithms.dqn","import","DQN","YourExternalEnv","=","...","register_env","(","``","my_env","''",",","lambda","config",":","YourExternalEnv","(","config",")",")","algo","=","DQN","(","env=","''","my_env","''",")","while","True",":","print","(","algo.train","(",")",")"]
docstr: Record an observation and get the on-policy action.
  
  Args:
      episode_id: Episode id returned from start_episode().
      observation: Current environment observation.
  
  Returns:
      Action from the env action space.
docstr_tok: ["Record","an","observation","and","get","the","on-policy","action",".","Args",":","episode_id",":","Episode","id","returned","from","start_episode","(",")",".","observation",":","Current","environment","observation",".","Returns",":","Action","from","the","env","action","space","."]
code: def get_action(
      self, episode_id: str, observation: EnvObsType
  ) -> EnvActionType:
      """Record an observation and get the on-policy action.
  
      Args:
          episode_id: Episode id returned from start_episode().
          observation: Current environment observation.
  
      Returns:
          Action from the env action space.
      """
  
      episode = self._get(episode_id)
      return episode.wait_for_action(observation)
  
imports: import gymnasium
  import queue
  import threading
  import uuid
  from typing import Callable, Tuple, Optional, TYPE_CHECKING
  from ray.rllib.env.base_env import BaseEnv
  from ray.rllib.utils.annotations import override, PublicAPI
  from ray.rllib.utils.typing import EnvActionType, EnvInfoDict, EnvObsType, EnvType, MultiEnvDict
  from ray.rllib.utils.deprecation import deprecation_warning
label_desc: Use image node_id 4 for calling the ExternalEnv obj's underlying member method code with example usage: obj.get_action(episode_id, observation) and returns: episode