⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/browsergym/workarena/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
__version__ = "0.5.1"
__version__ = "0.5.2"

# Check playwright version early to avoid cryptic errors
import importlib.metadata

_playwright_version = importlib.metadata.version("playwright")
if _playwright_version != "1.44.0":
raise RuntimeError(
f"browsergym-workarena requires playwright==1.44.0, but found {_playwright_version}. "
f"Please install the correct version: pip install playwright==1.44.0"
)

import inspect
from logging import warning
Expand Down
2 changes: 1 addition & 1 deletion src/browsergym/workarena/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

# Hugging Face dataset containing available instances
INSTANCE_REPO_ID = "ServiceNow/WorkArena-Instances"
INSTANCE_REPO_FILENAME = "instances.json"
INSTANCE_REPO_FILENAME = "instances_v2.json"
INSTANCE_REPO_TYPE = "dataset"
INSTANCE_XOR_SEED = "x3!+-9mi#nhlo%a02$9hna{]"

Expand Down
16 changes: 13 additions & 3 deletions src/browsergym/workarena/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,18 @@ def encrypt_instance_password(password: str) -> str:
return base64.b64encode(cipher_bytes).decode("utf-8")


def fetch_instances():
def fetch_instances(filename: str = None):
"""
Load the latest instances from either a custom pool (SNOW_INSTANCE_POOL env var) or the gated HF dataset.

Parameters:
-----------
filename: str
Optional filename to fetch from the HF dataset. Defaults to INSTANCE_REPO_FILENAME.
"""
if filename is None:
filename = INSTANCE_REPO_FILENAME

pool_path = os.getenv("SNOW_INSTANCE_POOL")
if pool_path:
path = os.path.expanduser(pool_path)
Expand All @@ -62,13 +70,13 @@ def fetch_instances():
disable_progress_bars()
path = hf_hub_download(
repo_id=INSTANCE_REPO_ID,
filename=INSTANCE_REPO_FILENAME,
filename=filename,
repo_type=INSTANCE_REPO_TYPE,
)
logging.info("Loaded ServiceNow instances from the default instance pool.")
except Exception as e:
raise RuntimeError(
f"Could not access {INSTANCE_REPO_ID}/{INSTANCE_REPO_FILENAME}. "
f"Could not access {INSTANCE_REPO_ID}/{filename}. "
"Make sure you have been granted access to the gated repo and that you are "
"authenticated (run `huggingface-cli login` or set HUGGING_FACE_HUB_TOKEN)."
) from e
Expand All @@ -77,6 +85,8 @@ def fetch_instances():
entries = json.load(f)

for entry in entries:
if entry.get("error"):
raise RuntimeError(entry.get("message", "Unknown error from instance pool"))
entry["url"] = entry["u"]
entry["password"] = decrypt_instance_password(entry["p"])
del entry["u"]
Expand Down
24 changes: 24 additions & 0 deletions src/browsergym/workarena/tasks/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,30 @@ def get_init_scripts(self) -> List[str]:

runInGsftMainOnlyAndProtectByURL(monitorChangeOnFields, '{url_suffix}');
""",
f"""
function removePersonalizeFormButton() {{
waLog('Searching for Personalize Form button...', 'removePersonalizeFormButton');
let button = document.querySelector('#togglePersonalizeForm');
if (button) {{
button.remove();
waLog('Removed Personalize Form button', 'removePersonalizeFormButton');
}}
}}

runInGsftMainOnlyAndProtectByURL(removePersonalizeFormButton, '{url_suffix}');
""",
f"""
function removeAdditionalActionsButton() {{
waLog('Searching for Additional Actions button...', 'removeAdditionalActionsButton');
let button = document.querySelector('button.additional-actions-context-menu-button');
if (button) {{
button.remove();
waLog('Removed Additional Actions button', 'removeAdditionalActionsButton');
}}
}}

runInGsftMainOnlyAndProtectByURL(removeAdditionalActionsButton, '{url_suffix}');
""",
]

def start(self, page: Page) -> None:
Expand Down
23 changes: 22 additions & 1 deletion src/browsergym/workarena/tasks/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,28 @@ def all_configs(cls) -> List[dict]:
return json.load(f)

def get_init_scripts(self) -> List[str]:
return super().get_init_scripts() + ["registerGsftMainLoaded();"]
return super().get_init_scripts() + [
"registerGsftMainLoaded();",
self._get_remove_personalize_list_button_script(),
]

def _get_remove_personalize_list_button_script(self):
"""
Removes the 'Personalize List' button on list pages.
"""
script = """
function removePersonalizeListButton() {
waLog('Searching for Personalize List buttons...', 'removePersonalizeListButton');
let buttons = document.querySelectorAll('i[data-type="list_mechanic2_open"]');
buttons.forEach((button) => {
button.remove();
});
waLog('Removed ' + buttons.length + ' Personalize List buttons', 'removePersonalizeListButton');
}

runInGsftMainOnlyAndProtectByURL(removePersonalizeListButton, '_list.do');
"""
return script

def _get_visible_list(self, page: Page):
self._wait_for_ready(page)
Expand Down
57 changes: 57 additions & 0 deletions src/browsergym/workarena/tasks/service_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ def get_init_scripts(self) -> List[str]:
"registerGsftMainLoaded()",
self._get_disable_add_to_cart_script(),
self._get_remove_top_items_panel_script(),
self._get_remove_add_content_button_script(),
self._get_remove_header_decorations_script(),
self._get_remove_more_options_buttons_script(),
]

def _get_disable_add_to_cart_script(self):
Expand Down Expand Up @@ -276,6 +279,60 @@ def _get_remove_top_items_panel_script(self):
"""
return script

def _get_remove_add_content_button_script(self):
"""
Removes the 'Add content' button from the service catalog page.
"""
script = """
function removeAddContentButton() {
waLog('Searching for Add content button...', 'removeAddContentButton');
let button = document.querySelector('button[aria-label="Add content"]');
if (button) {
button.remove();
waLog('Removed Add content button', 'removeAddContentButton');
}
}

runInGsftMainOnlyAndProtectByURL(removeAddContentButton, 'catalog_home');
"""
return script

def _get_remove_header_decorations_script(self):
"""
Removes all header decoration panels (edit/settings/close buttons) from the service catalog page.
"""
script = """
function removeHeaderDecorations() {
waLog('Searching for header decoration panels...', 'removeHeaderDecorations');
let panels = document.querySelectorAll('div.header_decorations');
panels.forEach((panel) => {
panel.remove();
});
waLog('Removed ' + panels.length + ' header decoration panels', 'removeHeaderDecorations');
}

runInGsftMainOnlyAndProtectByURL(removeHeaderDecorations, 'catalog_home');
"""
return script

def _get_remove_more_options_buttons_script(self):
"""
Removes all 'More Options' buttons from the service catalog page.
"""
script = """
function removeMoreOptionsButtons() {
waLog('Searching for More Options buttons...', 'removeMoreOptionsButtons');
let buttons = document.querySelectorAll('button.btn.btn-icon.icon-ellipsis');
buttons.forEach((button) => {
button.remove();
});
waLog('Removed ' + buttons.length + ' More Options buttons', 'removeMoreOptionsButtons');
}

runInGsftMainOnlyAndProtectByURL(removeMoreOptionsButtons, 'com.glideapp.servicecatalog');
"""
return script

def setup_goal(self, page: Page) -> tuple[str, dict]:
super().setup_goal(page=page)

Expand Down
Loading