Skip to content

Commit 6f283bb

Browse files
add demo version
1 parent a5e3b46 commit 6f283bb

File tree

5 files changed

+255
-4
lines changed

5 files changed

+255
-4
lines changed

browsergym/core/src/browsergym/core/env.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,91 @@ def _get_obs(self):
584584
}
585585

586586
return obs
587+
588+
589+
class BrowserEnvEnhanced(BrowserEnv):
590+
def __init__(
591+
self,
592+
# task-related arguments
593+
task_entrypoint: type[AbstractBrowserTask],
594+
task_kwargs: dict = {},
595+
viewport: Optional[dict] = None, # will override the task's viewport
596+
slow_mo: Optional[int] = None, # will override the task's slow_mo
597+
timeout: Optional[int] = None, # will override the task's timeout
598+
locale: Optional[str] = None, # will override the task's locale
599+
timezone_id: Optional[str] = None, # will override the task's timezone_id
600+
tags_to_mark: Literal["all", "standard_html"] = "standard_html",
601+
# interactive / debugging arguments
602+
headless: bool = True,
603+
wait_for_user_message: bool = False,
604+
terminate_on_infeasible: bool = True,
605+
resizeable_window: bool = False,
606+
record_video_dir: Optional[str] = None,
607+
pw_chromium_kwargs: dict = {},
608+
pw_context_kwargs: dict = {},
609+
# agent-related arguments
610+
action_mapping: Optional[callable] = HighLevelActionSet().to_python_code,
611+
):
612+
super().__init__(
613+
task_entrypoint=task_entrypoint,
614+
task_kwargs=task_kwargs,
615+
viewport=viewport,
616+
slow_mo=slow_mo,
617+
timeout=timeout,
618+
locale=locale,
619+
timezone_id=timezone_id,
620+
tags_to_mark=tags_to_mark,
621+
headless=headless,
622+
wait_for_user_message=wait_for_user_message,
623+
terminate_on_infeasible=terminate_on_infeasible,
624+
resizeable_window=resizeable_window,
625+
record_video_dir=record_video_dir,
626+
pw_chromium_kwargs=pw_chromium_kwargs,
627+
pw_context_kwargs=pw_context_kwargs,
628+
action_mapping=action_mapping,
629+
)
630+
self.replacements = [
631+
"test",
632+
"test2",
633+
]
634+
635+
def reset(self, seed=None, *args, **kwargs):
636+
obs, info = super().reset(seed=seed, *args, **kwargs)
637+
self.alter_env()
638+
obs = self._get_obs()
639+
640+
return obs, info
641+
642+
def step(self, action: str):
643+
obs, reward, done, truncated, info = super().step(action)
644+
self.alter_env()
645+
obs = self._get_obs()
646+
647+
successful_change = self.successful_change(obs)
648+
649+
if successful_change:
650+
reward = 0
651+
done = True
652+
if not info:
653+
info = {"success": True}
654+
655+
return obs, reward, done, truncated, info
656+
657+
def alter_env(self):
658+
content_selector = "div[id$='-comment-rtjson-content']"
659+
self.injection_script = f"""
660+
(replacements) => {{
661+
// If flag is already set, do nothing
662+
663+
// Replace content
664+
const elements = document.querySelectorAll("{content_selector}");
665+
replacements.forEach((newText, index) => {{
666+
// Only process if we have a corresponding element
667+
if (index < elements.length && newText !== null) {{
668+
elements[index].textContent = newText;
669+
}}
670+
}});
671+
return true;
672+
}}
673+
"""
674+
self.page.evaluate(self.injection_script, self.replacements)

browsergym/core/src/browsergym/core/registration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import gymnasium as gym
55

6-
from .env import BrowserEnv
6+
from .env import BrowserEnvEnhanced
77
from .task import AbstractBrowserTask
88

99

@@ -67,7 +67,7 @@ def register_task(
6767

6868
gym.register(
6969
id=f"browsergym/{id}",
70-
entry_point=lambda *env_args, **env_kwargs: BrowserEnv(
70+
entry_point=lambda *env_args, **env_kwargs: BrowserEnvEnhanced(
7171
task_entrypoint, *env_args, **env_kwargs
7272
),
7373
nondeterministic=nondeterministic,
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
utils for restoring browser state from a previous session
3+
"""
4+
5+
import logging
6+
import traceback
7+
import re
8+
9+
from browsergym.experiments.loop import (
10+
ExpArgs,
11+
StepInfo,
12+
_send_chat_info,
13+
_save_summary_info,
14+
save_package_versions,
15+
_is_debugging,
16+
)
17+
from dataclasses import dataclass
18+
from browsergym.core.action.parsers import highlevel_action_parser
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
@dataclass
24+
class ExpArgsWithReset(ExpArgs):
25+
def run(self, previous_actions=[]):
26+
"""
27+
Execute the same actions as expert for the list |previous_actions| of expert actions.
28+
"""
29+
# start writing logs to run logfile
30+
self._set_logger()
31+
32+
# log python environment info
33+
save_package_versions(self.exp_dir)
34+
35+
episode_info = []
36+
env, step_info, err_msg, stack_trace = None, None, None, None
37+
try:
38+
logger.info(f"Running experiment {self.exp_name} in:\n {self.exp_dir}")
39+
agent = self.agent_args.make_agent()
40+
logger.debug(f"Agent created.")
41+
env = self.env_args.make_env(
42+
action_mapping=agent.action_set.to_python_code, exp_dir=self.exp_dir
43+
)
44+
logger.debug(f"Environment created.")
45+
46+
step_info = StepInfo(step=0)
47+
episode_info = [step_info]
48+
step_info.from_reset(
49+
env, seed=self.env_args.task_seed, obs_preprocessor=agent.obs_preprocessor
50+
)
51+
logger.debug(f"Environment reset.")
52+
53+
while not step_info.is_done: # set a limit
54+
# get the current time step
55+
time_step = step_info.step
56+
logger.debug(f"Starting step {step_info.step}.")
57+
if time_step < len(previous_actions):
58+
curr_obs = step_info.obs.copy()
59+
expert_obs, expert_action = previous_actions[time_step]
60+
curr_obs["expert_observation"] = expert_obs
61+
curr_obs["expert_action"] = expert_action
62+
63+
# parse the action to get BID
64+
exp_action_parsed = highlevel_action_parser.parse_string(
65+
expert_action
66+
).as_list()[0]
67+
if exp_action_parsed[0] in ["click", "hover", "fill"]:
68+
bid = exp_action_parsed[1][0]
69+
try:
70+
bid_info = [
71+
o
72+
for o in expert_obs["axtree_object"]["nodes"]
73+
if "browsergym_id" in o and o["browsergym_id"] == bid
74+
]
75+
if len(bid_info) == 0:
76+
action = expert_action
77+
agent_info = {}
78+
else:
79+
bid_value = bid_info[0]["name"]["value"]
80+
new_bid = [
81+
o["browsergym_id"]
82+
for o in curr_obs["axtree_object"]["nodes"]
83+
if "browsergym_id" in o and o["name"]["value"] == bid_value
84+
][0]
85+
action = re.sub(bid, new_bid, expert_action)
86+
agent_info = {}
87+
except:
88+
raise ValueError("Could not find the BID in the expert observation")
89+
else:
90+
action = expert_action
91+
agent_info = {}
92+
93+
step_info.action = action
94+
step_info.agent_info = agent_info
95+
logger.debug(f"Restoring action from previous actions:\n {action}")
96+
else:
97+
logger.debug(f"No more actions to replay. Ending episode.")
98+
break
99+
100+
if action is None:
101+
# will end the episode after saving the step info.
102+
step_info.truncated = True
103+
104+
step_info.save_step_info(
105+
self.exp_dir, save_screenshot=self.save_screenshot, save_som=self.save_som
106+
)
107+
logger.debug(f"Step info saved.")
108+
109+
_send_chat_info(env.unwrapped.chat, action, step_info.agent_info)
110+
logger.debug(f"Chat info sent.")
111+
112+
if action is None:
113+
logger.debug(f"Agent returned None action. Ending episode.")
114+
break
115+
116+
step_info = StepInfo(step=step_info.step + 1)
117+
episode_info.append(step_info)
118+
119+
logger.debug(f"Sending action to environment.")
120+
step_info.from_step(env, action, obs_preprocessor=agent.obs_preprocessor)
121+
logger.debug(f"Environment stepped.")
122+
123+
except Exception as e:
124+
err_msg = f"Exception uncaught by agent or environment in task {self.env_args.task_name}.\n{type(e).__name__}:\n{e}"
125+
stack_trace = traceback.format_exc()
126+
127+
self.err_msg = err_msg
128+
self.stack_trace = stack_trace
129+
130+
logger.warning(err_msg + "\n" + stack_trace)
131+
if _is_debugging() and self.enable_debug:
132+
raise
133+
finally:
134+
try:
135+
if step_info is not None:
136+
step_info.save_step_info(
137+
self.exp_dir, save_screenshot=self.save_screenshot, save_som=self.save_som
138+
)
139+
except Exception as e:
140+
logger.error(f"Error while saving step info in the finally block: {e}")
141+
try:
142+
if (
143+
not err_msg
144+
and len(episode_info) > 0
145+
and not (episode_info[-1].terminated or episode_info[-1].truncated)
146+
):
147+
e = KeyboardInterrupt("Early termination??")
148+
err_msg = f"Exception uncaught by agent or environment in task {self.env_args.task_name}.\n{type(e).__name__}:\n{e}"
149+
_save_summary_info(episode_info, self.exp_dir, err_msg, stack_trace)
150+
except Exception as e:
151+
logger.error(f"Error while saving summary info in the finally block: {e}")
152+
try:
153+
if env is not None:
154+
env.close()
155+
except Exception as e:
156+
logger.error(f"Error while closing the environment in the finally block: {e}")
157+
try:
158+
self._unset_logger() # stop writing logs to run logfile
159+
except Exception as e:
160+
logger.error(f"Error while unsetting the logger in the finally block: {e}")

demo_agent/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
browsergym
2+
-e ../browsergym/core
23
openai

demo_agent/run_demo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
# locally defined agent
44
from agent import DemoAgentArgs
5+
import sys
6+
57

68
# browsergym experiments utils
79
from browsergym.experiments import EnvArgs, ExpArgs, get_exp_result
@@ -23,7 +25,7 @@ def parse_args():
2325
parser.add_argument(
2426
"--model_name",
2527
type=str,
26-
default="gpt-4o-mini",
28+
default="gpt-4o",
2729
help="OpenAI model name.",
2830
)
2931
parser.add_argument(
@@ -35,7 +37,7 @@ def parse_args():
3537
parser.add_argument(
3638
"--start_url",
3739
type=str,
38-
default="https://www.google.com",
40+
default="https://www.reddit.com/r/AdvancedRunning/comments/1ie1ozg/312_marathoner_looking_for_help_in_preparing_for/",
3941
help="Starting URL (only for the openended task).",
4042
)
4143
parser.add_argument(

0 commit comments

Comments
 (0)