diff --git a/src/browsergym/workarena/data_files/task_configs/create_workspace_incident_task.json b/src/browsergym/workarena/data_files/task_configs/create_workspace_incident_task.json new file mode 100644 index 0000000..7dd3121 --- /dev/null +++ b/src/browsergym/workarena/data_files/task_configs/create_workspace_incident_task.json @@ -0,0 +1,119 @@ +[ + { + "fields": { + "business_impact": "Business impact", + "caller_id": "Caller", + "category": "Category", + "caused_by": "Caused by Change", + "close_code": "Resolution code", + "close_notes": "Resolution notes", + "cmdb_ci": "Configuration item", + "contact_type": "Channel", + "description": "Description", + "impact": "Impact", + "knowledge": "Knowledge", + "number": "Number", + "parent_incident": "Parent Incident", + "short_description": "Short description", + "urgency": "Urgency", + "work_notes": "Work notes" + }, + "task_fields": [ + "caller_id", + "description", + "impact", + "short_description", + "business_impact" + ], + "template_record": { + "caller_id": "Rick Berzle", + "category": "Hardware", + "description": "Hard drive has been making a loud grinding noise for the last two days.", + "impact": "2 - Medium", + "severity": "3 - Low", + "short_description": "Seem to have an issue with my hard drive...", + "urgency": "3 - Low", + "business_impact": "Something is wrong" + } + }, + { + "fields": { + "business_impact": "Business impact", + "assignment_group": "Assignment group", + "business_service": "Service", + "caller_id": "Caller", + "category": "Category", + "caused_by": "Caused by Change", + "close_code": "Resolution code", + "close_notes": "Resolution notes", + "cmdb_ci": "Configuration item", + "contact_type": "Channel", + "description": "Description", + "impact": "Impact", + "knowledge": "Knowledge", + "number": "Number", + "parent_incident": "Parent Incident", + "problem_id": "Problem", + "resolved_by": "Resolved by", + "rfc": "Change Request", + "service_offering": "Service offering", + "short_description": "Short description", + "urgency": "Urgency", + "work_notes": "Work notes" + }, + "task_fields": [ + "caller_id", + "description", + "impact", + "short_description", + "business_impact" + ], + "template_record": { + "caller_id": "Fred Luddy", + "description": "The hotfix was installed on my PC and now some of my applications have stopped working. Can I roll back to the previous version?", + "impact": "1 - High", + "short_description": "Please remove the latest hotfix from my PC", + "business_impact": "We are working on it" + } + }, + { + "fields": { + "assignment_group": "Assignment group", + "business_impact": "Business impact", + "business_service": "Service", + "caller_id": "Caller", + "category": "Category", + "caused_by": "Caused by Change", + "close_code": "Resolution code", + "close_notes": "Resolution notes", + "cmdb_ci": "Configuration item", + "contact_type": "Channel", + "description": "Description", + "impact": "Impact", + "knowledge": "Knowledge", + "number": "Number", + "parent_incident": "Parent Incident", + "problem_id": "Problem", + "resolved_by": "Resolved by", + "rfc": "Change Request", + "service_offering": "Service offering", + "short_description": "Short description", + "urgency": "Urgency", + "work_notes": "Work notes" + }, + "task_fields": [ + "caller_id", + "description", + "impact", + "short_description", + "business_impact" + ], + "template_record": { + "caller_id": "Abel Tuter", + "description": "Forgot password and unable to log in. Can you reset or resend my password?", + "impact": "3 - Low", + "short_description": "Reset my password", + "business_impact": "trivial reset password" + } + } +] \ No newline at end of file diff --git a/src/browsergym/workarena/data_files/task_configs/create_workspace_problem_task.json b/src/browsergym/workarena/data_files/task_configs/create_workspace_problem_task.json new file mode 100644 index 0000000..4aad81e --- /dev/null +++ b/src/browsergym/workarena/data_files/task_configs/create_workspace_problem_task.json @@ -0,0 +1,518 @@ +[ + { + "fields": { + "assignment_group": "Assignment group", + "business_service": "Service", + "category": "Category", + "cmdb_ci": "Configuration item", + "description": "Description", + "impact": "Impact", + "service_offering": "Service offering", + "short_description": "Problem statement", + "urgency": "Urgency" + }, + "task_fields": [ + "short_description", + "impact", + "category", + "description" + ], + "template_record": { + "active": "false", + "activity_due": "UNKNOWN", + "additional_assignee_list": "", + "approval": "Not Yet Requested", + "approval_history": "", + "approval_set": "", + "assigned_to": { + "display_value": "Problem Coordinator B", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/38cb3f173b331300ad3cc9bb34efc4d6" + }, + "assignment_group": "", + "business_duration": "", + "business_service": "", + "calendar_duration": "", + "category": "Hardware", + "cause_notes": null, + "close_notes": "This is a Service Catalog item request", + "closed_at": "", + "closed_by": "", + "cmdb_ci": "", + "comments": "", + "comments_and_work_notes": "", + "company": "", + "confirmed_at": "", + "confirmed_by": "", + "contact_type": "Phone", + "contract": "", + "correlation_display": "", + "correlation_id": "", + "delivery_plan": "", + "delivery_task": "", + "description": "bizonal wateringly nonsuccessful checkerberry abridgeable", + "due_date": "", + "duplicate_of": "", + "escalation": "Normal", + "expected_start": "", + "first_reported_by_task": { + "display_value": "INC0000020", + "link": "https://dev215901.service-now.com/api/now/table/task/46edaa6aa9fe198101b9d14ced16619f" + }, + "fix_at": "", + "fix_by": "", + "fix_communicated_at": "", + "fix_communicated_by": "", + "fix_notes": null, + "follow_up": "", + "group_list": "", + "impact": "3 - Low", + "knowledge": "false", + "known_error": "false", + "location": "", + "made_sla": "false", + "major_problem": "false", + "number": "PRB0000003", + "opened_at": "2022-11-16 16:07:08", + "opened_by": { + "display_value": "Don Goodliffe", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/9ee1b13dc6112271007f9d0efdb69cd0" + }, + "order": "", + "parent": "", + "priority": "5 - Planning", + "problem_state": "Closed", + "reassignment_count": "", + "related_incidents": "1", + "reopen_count": "0", + "reopened_at": "", + "reopened_by": "", + "resolution_code": "Canceled", + "resolved_at": "", + "resolved_by": "", + "review_outcome": "", + "rfc": "", + "route_reason": "", + "service_offering": "", + "short_description": "Request for a Blackberry", + "sla_due": "UNKNOWN", + "state": "Closed", + "subcategory": null, + "sys_class_name": "Problem", + "sys_created_by": "david.loo", + "sys_created_on": "2022-11-16 16:07:08", + "sys_domain": { + "display_value": "global", + "link": "https://dev215901.service-now.com/api/now/table/sys_user_group/global" + }, + "sys_domain_path": "/", + "sys_id": "46fb9e31a9fe198101492060c2a4f8cb", + "sys_mod_count": "4", + "sys_tags": "", + "sys_updated_by": "admin", + "sys_updated_on": "2023-07-27 02:54:38", + "task_effective_number": "PRB0000003", + "time_worked": "", + "universal_request": "", + "upon_approval": null, + "upon_reject": null, + "urgency": "3 - Low", + "user_input": "", + "watch_list": "", + "work_end": "", + "work_notes": "", + "work_notes_list": "", + "work_start": "", + "workaround": null, + "workaround_applied": "false", + "workaround_communicated_at": "", + "workaround_communicated_by": "" + } + }, + { + "fields": { + "assignment_group": "Assignment group", + "business_service": "Service", + "category": "Category", + "cmdb_ci": "Configuration item", + "description": "Description", + "impact": "Impact", + "service_offering": "Service offering", + "short_description": "Problem statement", + "urgency": "Urgency" + }, + "task_fields": [ + "short_description", + "impact", + "category", + "description" + ], + "template_record": { + "active": "true", + "activity_due": "UNKNOWN", + "additional_assignee_list": "", + "approval": null, + "approval_history": "", + "approval_set": "", + "assigned_to": { + "display_value": "Problem Coordinator B", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/38cb3f173b331300ad3cc9bb34efc4d6" + }, + "assignment_group": "", + "business_duration": "", + "business_service": "", + "calendar_duration": "", + "category": "Network", + "cause_notes": null, + "close_notes": "", + "closed_at": "", + "closed_by": "", + "cmdb_ci": "", + "comments": "", + "comments_and_work_notes": "", + "company": "", + "confirmed_at": "2021-11-19 15:48:36", + "confirmed_by": { + "display_value": "Problem Coordinator B", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/38cb3f173b331300ad3cc9bb34efc4d6" + }, + "contact_type": null, + "contract": "", + "correlation_display": "", + "correlation_id": "", + "delivery_plan": "", + "delivery_task": "", + "description": "The 1st floor router is down again. Nobody down there can get at anything outside their own subnet.", + "due_date": "", + "duplicate_of": "", + "escalation": "High", + "expected_start": "", + "first_reported_by_task": { + "display_value": "INC0000001", + "link": "https://dev215901.service-now.com/api/now/table/task/9c573169c611228700193229fff72400" + }, + "fix_at": "", + "fix_by": "", + "fix_communicated_at": "", + "fix_communicated_by": "", + "fix_notes": null, + "follow_up": "", + "group_list": "", + "impact": "3 - Low", + "knowledge": "false", + "known_error": "false", + "location": "", + "made_sla": "false", + "major_problem": "false", + "number": "PRB0000007", + "opened_at": "2021-06-04 15:32:07", + "opened_by": { + "display_value": "Don Goodliffe", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/9ee1b13dc6112271007f9d0efdb69cd0" + }, + "order": "", + "parent": "", + "priority": "5 - Planning", + "problem_state": "Assess", + "reassignment_count": "", + "related_incidents": "2", + "reopen_count": "0", + "reopened_at": "", + "reopened_by": "", + "resolution_code": null, + "resolved_at": "", + "resolved_by": "", + "review_outcome": "", + "rfc": "", + "route_reason": "", + "service_offering": "", + "short_description": "Router Down", + "sla_due": "UNKNOWN", + "state": "Assess", + "subcategory": null, + "sys_class_name": "Problem", + "sys_created_by": "pat", + "sys_created_on": "2021-06-04 15:32:07", + "sys_domain": { + "display_value": "global", + "link": "https://dev215901.service-now.com/api/now/table/sys_user_group/global" + }, + "sys_domain_path": "/", + "sys_id": "9d3a266ac6112287004e37fb2ceb0133", + "sys_mod_count": "34", + "sys_tags": "", + "sys_updated_by": "admin", + "sys_updated_on": "2024-03-11 11:39:12", + "task_effective_number": "PRB0000007", + "time_worked": "", + "universal_request": "", + "upon_approval": null, + "upon_reject": null, + "urgency": "3 - Low", + "user_input": "", + "watch_list": "", + "work_end": "", + "work_notes": "", + "work_notes_list": "", + "work_start": "", + "workaround": null, + "workaround_applied": "false", + "workaround_communicated_at": "", + "workaround_communicated_by": "" + } + }, + { + "fields": { + "assignment_group": "Assignment group", + "business_service": "Service", + "category": "Category", + "cmdb_ci": "Configuration item", + "description": "Description", + "impact": "Impact", + "service_offering": "Service offering", + "short_description": "Problem statement", + "urgency": "Urgency" + }, + "task_fields": [ + "short_description", + "impact", + "category", + "description" + ], + "template_record": { + "active": "true", + "activity_due": "UNKNOWN", + "additional_assignee_list": "", + "approval": "Not Yet Requested", + "approval_history": "", + "approval_set": "", + "assigned_to": { + "display_value": "Problem Coordinator A", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/73ab3f173b331300ad3cc9bb34efc4df" + }, + "assignment_group": "", + "business_duration": "", + "business_service": "", + "calendar_duration": "", + "category": "Software", + "cause_notes": null, + "close_notes": "", + "closed_at": "", + "closed_by": "", + "cmdb_ci": "Email", + "comments": "", + "comments_and_work_notes": "", + "company": "", + "confirmed_at": "", + "confirmed_by": "", + "contact_type": null, + "contract": "", + "correlation_display": "", + "correlation_id": "", + "delivery_plan": "", + "delivery_task": "", + "description": "Unable to send or receive emails.Looks like issue is with the email server.", + "due_date": "", + "duplicate_of": "", + "escalation": "Normal", + "expected_start": "", + "first_reported_by_task": "", + "fix_at": "", + "fix_by": "", + "fix_communicated_at": "", + "fix_communicated_by": "", + "fix_notes": null, + "follow_up": "", + "group_list": "", + "impact": "3 - Low", + "knowledge": "false", + "known_error": "false", + "location": "", + "made_sla": "true", + "major_problem": "false", + "number": "PRB0007601", + "opened_at": "2018-08-30 01:08:39", + "opened_by": { + "display_value": "System Administrator", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/6816f79cc0a8016401c5a33be04be441" + }, + "order": "", + "parent": "", + "priority": "5 - Planning", + "problem_state": "New", + "reassignment_count": "0", + "related_incidents": "0", + "reopen_count": "0", + "reopened_at": "", + "reopened_by": "", + "resolution_code": null, + "resolved_at": "", + "resolved_by": "", + "review_outcome": "", + "rfc": "", + "route_reason": "", + "service_offering": "", + "short_description": "Unable to send or receive emails.", + "sla_due": "UNKNOWN", + "state": "New", + "subcategory": "Email", + "sys_class_name": "Problem", + "sys_created_by": "admin", + "sys_created_on": "2018-08-30 01:09:05", + "sys_domain": { + "display_value": "global", + "link": "https://dev215901.service-now.com/api/now/table/sys_user_group/global" + }, + "sys_domain_path": "/", + "sys_id": "62304320731823002728660c4cf6a7e8", + "sys_mod_count": "1", + "sys_tags": "", + "sys_updated_by": "admin", + "sys_updated_on": "2018-12-11 23:16:57", + "task_effective_number": "PRB0007601", + "time_worked": "", + "universal_request": "", + "upon_approval": "Proceed to Next Task", + "upon_reject": "Cancel all future Tasks", + "urgency": "3 - Low", + "user_input": "", + "watch_list": "", + "work_end": "", + "work_notes": "", + "work_notes_list": "", + "work_start": "", + "workaround": null, + "workaround_applied": "false", + "workaround_communicated_at": "", + "workaround_communicated_by": "" + } + }, + { + "fields": { + "assignment_group": "Assignment group", + "business_service": "Service", + "category": "Category", + "cmdb_ci": "Configuration item", + "description": "Description", + "impact": "Impact", + "service_offering": "Service offering", + "short_description": "Problem statement", + "urgency": "Urgency" + }, + "task_fields": [ + "short_description", + "impact", + "category", + "description" + ], + "template_record": { + "active": "true", + "activity_due": "UNKNOWN", + "additional_assignee_list": "", + "approval": "Not Yet Requested", + "approval_history": "", + "approval_set": "", + "assigned_to": { + "display_value": "Problem Coordinator A", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/73ab3f173b331300ad3cc9bb34efc4df" + }, + "assignment_group": "Problem Solving", + "business_duration": "", + "business_service": "", + "calendar_duration": "", + "category": "Hardware", + "cause_notes": null, + "close_notes": "", + "closed_at": "", + "closed_by": "", + "cmdb_ci": "Dell Wireless WLAN Utility", + "comments": "", + "comments_and_work_notes": "", + "company": "", + "confirmed_at": "2023-09-12 10:04:43", + "confirmed_by": { + "display_value": "Problem Coordinator A", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/73ab3f173b331300ad3cc9bb34efc4df" + }, + "contact_type": null, + "contract": "", + "correlation_display": "", + "correlation_id": "", + "delivery_plan": "", + "delivery_task": "", + "description": "Using a Dell Wireless WLAN Utility I cannot enable the option to disable wireless when plugin into an Ethernet port", + "due_date": "", + "duplicate_of": "", + "escalation": "Normal", + "expected_start": "", + "first_reported_by_task": "", + "fix_at": "", + "fix_by": "", + "fix_communicated_at": "", + "fix_communicated_by": "", + "fix_notes": null, + "follow_up": "", + "group_list": "", + "impact": "2 - Medium", + "knowledge": "false", + "known_error": "true", + "location": "", + "made_sla": "true", + "major_problem": "false", + "number": "PRB0000012", + "opened_at": "2023-09-12 10:04:43", + "opened_by": { + "display_value": "Don Goodliffe", + "link": "https://dev215901.service-now.com/api/now/table/sys_user/9ee1b13dc6112271007f9d0efdb69cd0" + }, + "order": "", + "parent": "", + "priority": "4 - Low", + "problem_state": "Assess", + "reassignment_count": "1", + "related_incidents": "", + "reopen_count": "0", + "reopened_at": "", + "reopened_by": "", + "resolution_code": null, + "resolved_at": "", + "resolved_by": "", + "review_outcome": "", + "rfc": "", + "route_reason": "", + "service_offering": "", + "short_description": "Cannot disable wireless when plug into an Ethernet port", + "sla_due": "UNKNOWN", + "state": "Assess", + "subcategory": null, + "sys_class_name": "Problem", + "sys_created_by": "admin", + "sys_created_on": "2023-09-12 10:12:12", + "sys_domain": { + "display_value": "global", + "link": "https://dev215901.service-now.com/api/now/table/sys_user_group/global" + }, + "sys_domain_path": "/", + "sys_id": "e9b0e55567b032004792adab9485ef6c", + "sys_mod_count": "3", + "sys_tags": "", + "sys_updated_by": "admin", + "sys_updated_on": "2023-07-27 23:14:22", + "task_effective_number": "PRB0000012", + "time_worked": "", + "universal_request": "", + "upon_approval": "Proceed to Next Task", + "upon_reject": "Cancel all future Tasks", + "urgency": "3 - Low", + "user_input": "", + "watch_list": "", + "work_end": "", + "work_notes": "", + "work_notes_list": "", + "work_start": "", + "workaround": null, + "workaround_applied": "false", + "workaround_communicated_at": "", + "workaround_communicated_by": "" + } + } +] \ No newline at end of file diff --git a/src/browsergym/workarena/tasks/form_workspace.py b/src/browsergym/workarena/tasks/form_workspace.py new file mode 100644 index 0000000..9da49da --- /dev/null +++ b/src/browsergym/workarena/tasks/form_workspace.py @@ -0,0 +1,188 @@ +from .form import CreateIncidentTask, CreateProblemTask +from playwright.sync_api import Page +from .utils.utils import prettyprint_enum +from .form import GenericNewRecordTask +from importlib import resources +from ..config import data_files +import logging +from typing import Tuple +from ..api.utils import table_api_call + + +class GenericCreateWorkspaceTask(GenericNewRecordTask): + + def setup_goal(self, page: Page) -> tuple[str, dict]: + super().setup_goal(page=page) + + # Get the task configuration + assert self.all_configs is not None, "No configuration available for the task." + config = self.fixed_config if self.fixed_config else self.random.choice(self.all_configs) + self.config = config + # If fixed_config is not None we already set the required attributes in the constructor + if self.fixed_config is None: + self._set_required_config_attributes(config) + self.protected_fields = self.task_fields + # Generate the goal + goal = ( + f"In the Service Operations workspace, create a new {self.table_label} with " + + prettyprint_enum( + [ + f'a value of "{self.template_record[f]}"' + + f' for field "{self.config["fields"][f]}"' + for f in self.task_fields + ] + ) + + "." + ) + info = {} + + return goal, info + + def validate( + self, page: Page, chat_messages: list[str] + ) -> Tuple[float, bool, str, dict]: + # get the url of the current page + if f"sow/record/{self.table_name}/" not in page.url: + return 0.0, False, "", {"message": f"The assistant is not in the workspace {self.table_label} form."} + else: + # Get the string after sow/record// in the url but before the /params + # This may not be a valid sys id, which does not matter as no record will be found with it + sys_id = page.url.split(f"sow/record/{self.table_name}/")[1].split("/params")[0] + + # Check that a record has actually been created + if sys_id is None: + logging.info("No record has been created or the form has not been submitted.") + return ( + 0, + False, + "", + {"message": "No record has been created or the form has not been submitted."}, + ) + + print(f"Found sys_id {sys_id} in the URL") + + # Add the sysid to the list of created sysids + # This is used to clean up the database after the task is completed. + self.created_sysids.append(sys_id) + + # Pull the record from the database + # XXX: It's possible that the record is not found, e.g., if form submission was rejected due to client-side + # validation errors. In this case, we should not raise an error and simply consider that no record was + # created. This is non-terminal for the task. + record = table_api_call( + instance=self.instance, + table=self.table_name, + params={ + "sysparm_query": f"sys_id={sys_id}", + "sysparm_display_value": True, + }, + wait_for_record=True, + max_retries=20, # Wait up to 10 seconds + raise_on_wait_expired=False, + )["result"] + + # This can happen if the form was submitted but was rejected due to invalid inputs (e.g., missing mandatory fields) + if len(record) == 0: + logging.info( + "The record was not found in the database. Perhaps the form was not submitted correctly. " + + sys_id, + ) + return ( + 0, + False, + "", + { + "message": "The record was not found in the database. Perhaps the form was not submitted correctly." + }, + ) + + # Extract display values for reference fields + record = { + f: v if not isinstance(v, dict) else v["display_value"] for f, v in record[0].items() + } + + # Check that the record matches the expected values + for f in self.task_fields: + if record[f].strip() != self.template_record[f].strip(): + error_msg = f'The field "{self.config["fields"][f]}" has the wrong value. Expected: "{self.template_record[f].strip()}", got: "{record[f].strip()}".' + logging.info(error_msg) + return ( + 0, + True, + error_msg, + {"message": error_msg}, + ) + + return ( + 1, + True, + "Nice work, thank you!", + {"message": "The record was successfully created."}, + ) + + def _wait_for_ready(self, page: Page, iframe_only=False) -> None: + """ + Waits for the main iframe and APIs to be fully loaded + + Parameters: + ---------- + page: playwright.sync_api.Page + The page on which to wait for the iframe to be loaded + iframe_only: bool + If True, only wait for the iframe to be loaded. If False, also wait for the APIs to be available. + + """ + logging.debug(f"Waiting for {self.js_prefix} to be fully loaded") + try: + page.wait_for_function( + f"typeof window.{self.js_prefix} !== 'undefined' && window.{self.js_prefix}.WORKARENA_LOAD_COMPLETE", + ) + except: + page.wait_for_load_state("networkidle") + return + logging.debug(f"Detected {self.js_prefix} ready") + + +class CreateWorkspaceIncidentTask(CreateIncidentTask, GenericCreateWorkspaceTask): + config_path = str( + resources.files(data_files).joinpath("task_configs/create_workspace_incident_task.json") + ) + + def __init__(self, seed: int = None, + instance=None, + fixed_config: dict = None, + check_record_created=True, + **kwargs) -> None: + + super().__init__( + seed=seed, + instance=instance, + fixed_config=fixed_config, + check_record_created=check_record_created) + + # Force starting at the homepage + self.start_url = self.instance.snow_url + + +class CreateWorkspaceProblemTask(CreateProblemTask, GenericCreateWorkspaceTask): + config_path = str( + resources.files(data_files).joinpath("task_configs/create_workspace_problem_task.json") + ) + + def __init__(self, seed: int = None, + instance=None, + fixed_config: dict = None, + check_record_created=True, + **kwargs) -> None: + + super().__init__( + seed=seed, + instance=instance, + fixed_config=fixed_config, + check_record_created=check_record_created) + + # Force starting at the homepage + self.start_url = self.instance.snow_url + + +__TASKS__ = [CreateWorkspaceIncidentTask, CreateWorkspaceProblemTask] \ No newline at end of file