2424logger = logging .getLogger (__name__ )
2525
2626
27+ def _to_jsonable (value : Any ) -> Any :
28+ """Convert BrowserGym payload values into JSON-safe Python objects."""
29+ if value is None or isinstance (value , (str , int , float , bool )):
30+ return value
31+ if isinstance (value , dict ):
32+ return {str (key ): _to_jsonable (item ) for key , item in value .items ()}
33+ if isinstance (value , (list , tuple )):
34+ return [_to_jsonable (item ) for item in value ]
35+ if hasattr (value , "tolist" ):
36+ try :
37+ return _to_jsonable (value .tolist ())
38+ except Exception : # noqa: BLE001 - third-party array-likes
39+ pass
40+ if hasattr (value , "item" ):
41+ try :
42+ return _to_jsonable (value .item ())
43+ except Exception : # noqa: BLE001 - third-party scalar-likes
44+ pass
45+ return str (value )
46+
47+
2748def _get_axtree_txt (obs : Dict [str , Any ]) -> str :
2849 """Extract accessibility tree text from BrowserGym observation.
2950
@@ -106,6 +127,7 @@ def __init__(
106127 viewport_width : int = 1280 ,
107128 viewport_height : int = 720 ,
108129 timeout : float = 10000.0 ,
130+ include_screenshot : bool = False ,
109131 ** gym_kwargs : Any ,
110132 ):
111133 """Initialize the BrowserGym environment.
@@ -128,14 +150,9 @@ def __init__(
128150 self .viewport_width = viewport_width
129151 self .viewport_height = viewport_height
130152 self .timeout = timeout
153+ self .include_screenshot = include_screenshot
131154 self .gym_kwargs = dict (gym_kwargs )
132155
133- # Build environment ID
134- if task_name :
135- self .env_id = f"browsergym/{ benchmark } .{ task_name } "
136- else :
137- self .env_id = f"browsergym/{ benchmark } "
138-
139156 # force import the benchmark module
140157 benchmark_modules = {
141158 "miniwob" : "browsergym.miniwob" ,
@@ -158,23 +175,8 @@ def __init__(
158175 )
159176 raise ValueError (message ) from import_error
160177
161- # Create the BrowserGym environment
162- try :
163- self .gym_env = gym .make (
164- self .env_id ,
165- headless = headless ,
166- viewport = {"width" : viewport_width , "height" : viewport_height },
167- timeout = timeout ,
168- ** self .gym_kwargs ,
169- )
170- except Exception as e : # noqa: BLE001 - gym.make
171- message = (
172- "Failed to create BrowserGym environment "
173- f"'{ self .env_id } ': { e } \n "
174- "Make sure the benchmark package is installed "
175- f"(e.g., pip install browsergym-{ benchmark } )."
176- )
177- raise ValueError (message ) from e
178+ self .env_id = self ._build_env_id (task_name )
179+ self .gym_env = self ._create_gym_env (self .env_id )
178180
179181 # State tracking
180182 self ._state = BrowserGymState (
@@ -187,6 +189,51 @@ def __init__(
187189 self ._last_obs : Optional [Dict [str , Any ]] = None
188190 self ._last_info : Optional [Dict [str , Any ]] = None
189191
192+ def _build_env_id (self , task_name : Optional [str ]) -> str :
193+ if task_name :
194+ return f"browsergym/{ self .benchmark } .{ task_name } "
195+ return f"browsergym/{ self .benchmark } "
196+
197+ def _create_gym_env (self , env_id : str ):
198+ try :
199+ return gym .make (
200+ env_id ,
201+ headless = self .headless ,
202+ viewport = {
203+ "width" : self .viewport_width ,
204+ "height" : self .viewport_height ,
205+ },
206+ timeout = self .timeout ,
207+ ** self .gym_kwargs ,
208+ )
209+ except Exception as e : # noqa: BLE001 - gym.make
210+ message = (
211+ "Failed to create BrowserGym environment "
212+ f"'{ env_id } ': { e } \n "
213+ "Make sure the benchmark package is installed "
214+ f"(e.g., pip install browsergym-{ self .benchmark } )."
215+ )
216+ raise ValueError (message ) from e
217+
218+ def _switch_task_if_needed (self , requested_task : str ) -> None :
219+ if requested_task == (self .task_name or "" ):
220+ return
221+
222+ next_task_name = requested_task or None
223+ next_env_id = self ._build_env_id (next_task_name )
224+ next_gym_env = self ._create_gym_env (next_env_id )
225+
226+ previous_gym_env = getattr (self , "gym_env" , None )
227+ self .task_name = next_task_name
228+ self .env_id = next_env_id
229+ self .gym_env = next_gym_env
230+
231+ if previous_gym_env is not None :
232+ try :
233+ previous_gym_env .close ()
234+ except Exception as exc : # noqa: BLE001 - browsergym/playwright cleanup
235+ logger .warning ("BrowserGym cleanup failed during task switch: %s" , exc )
236+
190237 def reset (
191238 self ,
192239 seed : Optional [int ] = None ,
@@ -202,11 +249,14 @@ def reset(
202249 Initial observation for the task
203250 """
204251 # Generate new episode ID
252+ resolved_task_name = task_name or self .task_name or ""
253+ self ._switch_task_if_needed (resolved_task_name )
254+
205255 self ._state = BrowserGymState (
206256 episode_id = str (uuid4 ()),
207257 step_count = 0 ,
208258 benchmark = self .benchmark ,
209- task_name = task_name or self . task_name or "" ,
259+ task_name = resolved_task_name ,
210260 )
211261
212262 # Reset options
@@ -343,7 +393,9 @@ def _create_observation(
343393 self ._state .goal = goal
344394
345395 # Extract additional observation modalities
346- screenshot = obs .get ("screenshot" ) if isinstance (obs , dict ) else None
396+ screenshot = None
397+ if self .include_screenshot and isinstance (obs , dict ):
398+ screenshot = _to_jsonable (obs .get ("screenshot" ))
347399
348400 # Extract last_action_error from obs (BrowserGym includes this)
349401 last_action_error = False
@@ -357,11 +409,11 @@ def _create_observation(
357409 if isinstance (obs , dict ):
358410 # Include useful fields but exclude large raw objects
359411 browsergym_metadata ["browsergym_obs" ] = {
360- k : v
412+ k : _to_jsonable ( v )
361413 for k , v in obs .items ()
362414 if k not in ("dom_object" , "axtree_object" , "screenshot" )
363415 }
364- browsergym_metadata ["browsergym_info" ] = info
416+ browsergym_metadata ["browsergym_info" ] = _to_jsonable ( info )
365417
366418 return BrowserGymObservation (
367419 text = text ,
0 commit comments