browsergym-workarena 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- browsergym/workarena/__init__.py +13 -1
- browsergym/workarena/api/category.py +74 -0
- browsergym/workarena/api/change_request.py +87 -0
- browsergym/workarena/api/computer_asset.py +90 -0
- browsergym/workarena/api/cost_center.py +19 -0
- browsergym/workarena/api/expense_line.py +89 -0
- browsergym/workarena/api/incident.py +45 -0
- browsergym/workarena/api/knowledge.py +29 -0
- browsergym/workarena/api/problem.py +90 -0
- browsergym/workarena/api/report.py +183 -0
- browsergym/workarena/api/requested_items.py +63 -0
- browsergym/workarena/api/user.py +11 -8
- browsergym/workarena/api/utils.py +47 -3
- browsergym/workarena/config.py +21 -1
- browsergym/workarena/data_files/setup_files/forms/expected_incident_form_fields.json +1 -1
- browsergym/workarena/data_files/setup_files/forms/expected_request_item_form_fields.json +1 -0
- browsergym/workarena/data_files/setup_files/knowledge/protocols.json +46 -0
- browsergym/workarena/data_files/setup_files/knowledge/test.html +1 -0
- browsergym/workarena/data_files/setup_files/lists/expected_asset_list_columns.json +2 -24
- browsergym/workarena/data_files/setup_files/lists/expected_change_request_list_columns.json +4 -40
- browsergym/workarena/data_files/setup_files/lists/expected_expense_line_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_hardware_list_columns.json +1 -42
- browsergym/workarena/data_files/setup_files/lists/expected_incident_list_columns.json +2 -18
- browsergym/workarena/data_files/setup_files/lists/expected_problem_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_requested_items_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_service_catalog_list_columns.json +2 -19
- browsergym/workarena/data_files/setup_files/lists/expected_user_list_columns.json +3 -50
- browsergym/workarena/data_files/task_configs/all_menu.json +1 -1
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_minmax_task.json +1 -1
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_value_task.json +1 -1
- browsergym/workarena/data_files/task_configs/filter_service_catalog_item_list_task.json +1 -1
- browsergym/workarena/data_files/task_configs/impersonation_users.json +1 -1
- browsergym/workarena/data_files/task_configs/report_retrieval_minmax_task.json +1 -1
- browsergym/workarena/data_files/task_configs/report_retrieval_value_task.json +1 -1
- browsergym/workarena/human_eval/console.js +176 -0
- browsergym/workarena/human_eval/tool.py +366 -0
- browsergym/workarena/install.py +81 -20
- browsergym/workarena/tasks/base.py +55 -20
- browsergym/workarena/tasks/comp_building_block.py +4 -0
- browsergym/workarena/tasks/compositional/__init__.py +76 -0
- browsergym/workarena/tasks/compositional/base.py +364 -0
- browsergym/workarena/tasks/compositional/dash_do_base.py +1366 -0
- browsergym/workarena/tasks/compositional/dash_do_catalog.py +1127 -0
- browsergym/workarena/tasks/compositional/dash_do_catalog_infeasible.py +2047 -0
- browsergym/workarena/tasks/compositional/dash_do_create_incident.py +403 -0
- browsergym/workarena/tasks/compositional/dash_do_create_incident_infeasible.py +278 -0
- browsergym/workarena/tasks/compositional/dash_do_create_problem.py +336 -0
- browsergym/workarena/tasks/compositional/dash_do_create_problem_infeasible.py +235 -0
- browsergym/workarena/tasks/compositional/dash_do_filter.py +1600 -0
- browsergym/workarena/tasks/compositional/dash_do_request_item.py +1315 -0
- browsergym/workarena/tasks/compositional/dash_do_request_item_infeasible.py +693 -0
- browsergym/workarena/tasks/compositional/delete_record.py +341 -0
- browsergym/workarena/tasks/compositional/edit_knowledge_base.py +457 -0
- browsergym/workarena/tasks/compositional/expense_management.py +598 -0
- browsergym/workarena/tasks/compositional/filter_and_do.py +139 -0
- browsergym/workarena/tasks/compositional/find_and_order_item.py +345 -0
- browsergym/workarena/tasks/compositional/manage_change_request_schedule.py +1417 -0
- browsergym/workarena/tasks/compositional/mark_duplicate_problems.py +499 -0
- browsergym/workarena/tasks/compositional/maximize_investment_return.py +1763 -0
- browsergym/workarena/tasks/compositional/navigate_and_do.py +1151 -0
- browsergym/workarena/tasks/compositional/navigate_and_do_infeasible.py +2100 -0
- browsergym/workarena/tasks/compositional/offboard_user.py +207 -0
- browsergym/workarena/tasks/compositional/onboard_user.py +226 -0
- browsergym/workarena/tasks/compositional/update_task.py +145 -0
- browsergym/workarena/tasks/compositional/utils/curriculum.py +215 -0
- browsergym/workarena/tasks/compositional/utils/infeasible_configs.py +151 -0
- browsergym/workarena/tasks/compositional/utils/knapsack.py +192 -0
- browsergym/workarena/tasks/compositional/warranty_check.py +227 -0
- browsergym/workarena/tasks/compositional/work_assignment.py +804 -0
- browsergym/workarena/tasks/compositional/workload_balancing.py +396 -0
- browsergym/workarena/tasks/dashboard.py +188 -8
- browsergym/workarena/tasks/form.py +1024 -232
- browsergym/workarena/tasks/knowledge.py +216 -25
- browsergym/workarena/tasks/list.py +519 -102
- browsergym/workarena/tasks/mark_duplicate_problem.py +171 -0
- browsergym/workarena/tasks/navigation.py +55 -13
- browsergym/workarena/tasks/scripts/extract_all_menu_items.py +9 -2
- browsergym/workarena/tasks/scripts/generate_dashboard_configs.py +6 -5
- browsergym/workarena/tasks/scripts/service_catalog.py +2 -1
- browsergym/workarena/tasks/scripts/validate.py +8 -2
- browsergym/workarena/tasks/send_chat_message.py +90 -0
- browsergym/workarena/tasks/service_catalog.py +94 -26
- browsergym/workarena/tasks/utils/form.py +1 -4
- browsergym/workarena/tasks/utils/private_tasks.py +63 -0
- browsergym/workarena/tasks/utils/utils.py +13 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/METADATA +19 -18
- browsergym_workarena-0.3.0.dist-info/RECORD +138 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/entry_points.txt +1 -0
- browsergym_workarena-0.2.1.dist-info/RECORD +0 -85
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/WHEEL +0 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import json
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
from faker import Faker
|
|
6
|
+
from playwright.sync_api._generated import Page
|
|
7
|
+
from typing import Tuple
|
|
8
|
+
|
|
9
|
+
fake = Faker()
|
|
10
|
+
|
|
11
|
+
from ...api.knowledge import give_kb_read_permissions
|
|
12
|
+
from ...api.utils import table_api_call
|
|
13
|
+
from ..base import AbstractServiceNowTask
|
|
14
|
+
from .base import CompositionalTask
|
|
15
|
+
from ...config import KB_FILEPATH, PROTOCOL_KB_NAME
|
|
16
|
+
from ...instance import SNowInstance
|
|
17
|
+
from ..knowledge import KnowledgeBaseSearchTask, AddCommentToKnowledgeArticleTask
|
|
18
|
+
from ..navigation import AllMenuTask
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class EditKnowledgeBaseTask(CompositionalTask):
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
seed: int = None,
|
|
25
|
+
instance: SNowInstance = None,
|
|
26
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
27
|
+
level: int = 3,
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Create a compositional task with specific subtasks
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
-----------
|
|
34
|
+
instance: SNowInstance
|
|
35
|
+
The ServiceNow instance to run the task on.
|
|
36
|
+
fixed_config: list[AbstractServiceNowTask]
|
|
37
|
+
A list of tuples, each containing a subtask, its configuration and whether or not it should be validated.
|
|
38
|
+
level: int
|
|
39
|
+
The level of the task; choice between 2 and 3. L2 will have all the info in the the goal and start in the SNOW home page.
|
|
40
|
+
L3 will start in a private task page describing the information needed to complete the task and the related company protocol
|
|
41
|
+
to complete it.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
-----------
|
|
45
|
+
task_description: str
|
|
46
|
+
The start of the task description to be completed. e.g. "Referring to company protocol 'Edit a knowledge article', edit the knowledge base to handle the incorrect information: \n'
|
|
47
|
+
short_description: str
|
|
48
|
+
A short description of the task to be completed. e.g. "Edit knowledge base entries for address of parking lot."
|
|
49
|
+
"""
|
|
50
|
+
assert level in [2, 3], "Level must be either 2 or 3"
|
|
51
|
+
self.level = level
|
|
52
|
+
self.protocol_name = "Edit a knowledge article to manage incorrect information"
|
|
53
|
+
|
|
54
|
+
super().__init__(
|
|
55
|
+
seed=seed,
|
|
56
|
+
instance=instance,
|
|
57
|
+
fixed_config=fixed_config,
|
|
58
|
+
level=level,
|
|
59
|
+
protocol_name=self.protocol_name,
|
|
60
|
+
user_roles=["itil"], # Required permission to access service desk for l3
|
|
61
|
+
)
|
|
62
|
+
with open(KB_FILEPATH, "r") as f:
|
|
63
|
+
self.kb_entries = json.load(f)
|
|
64
|
+
if not hasattr(self, "_base_initial_instance"):
|
|
65
|
+
self._base_initial_instance = self.instance
|
|
66
|
+
self.adhoc_kb_name = None
|
|
67
|
+
self.task_description = None
|
|
68
|
+
self.short_description = None
|
|
69
|
+
|
|
70
|
+
def create_adhoc_kb(self):
|
|
71
|
+
user_full_name = " ".join(self._base_user_name.split(".")[:-1])
|
|
72
|
+
adhoc_kb_name = f"{user_full_name}'s Knowledge Base"
|
|
73
|
+
self.adhoc_kb_name = adhoc_kb_name
|
|
74
|
+
|
|
75
|
+
kb = table_api_call(
|
|
76
|
+
instance=self._base_initial_instance,
|
|
77
|
+
table="kb_knowledge_base",
|
|
78
|
+
method="POST",
|
|
79
|
+
data=json.dumps(
|
|
80
|
+
{
|
|
81
|
+
"title": self.adhoc_kb_name,
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
)["result"]
|
|
85
|
+
|
|
86
|
+
return kb["sys_id"]
|
|
87
|
+
|
|
88
|
+
def get_random_article_name(self):
|
|
89
|
+
kb = table_api_call(
|
|
90
|
+
instance=self.instance,
|
|
91
|
+
table="kb_knowledge",
|
|
92
|
+
params={
|
|
93
|
+
"sysparm_query": f"kb_knowledge_base={self.adhoc_kb_sys_id}",
|
|
94
|
+
"sysparm_fields": "short_description",
|
|
95
|
+
},
|
|
96
|
+
)["result"]
|
|
97
|
+
self.article_titles = [kb_article["short_description"] for kb_article in kb]
|
|
98
|
+
|
|
99
|
+
article_date = fake.date_this_year().strftime("%Y-%m-%d")
|
|
100
|
+
base_article_name = self.base_config["item"].capitalize()
|
|
101
|
+
article_title = f"{base_article_name}-{article_date}"
|
|
102
|
+
while article_title in self.article_titles:
|
|
103
|
+
article_date = fake.date_this_year().strftime("%Y-%m-%d")
|
|
104
|
+
article_title = f"{base_article_name}-{article_date}"
|
|
105
|
+
|
|
106
|
+
return article_title
|
|
107
|
+
|
|
108
|
+
def create_article(self, article_name, article_text):
|
|
109
|
+
if article_name in self.article_titles:
|
|
110
|
+
raise Exception("Article with the name already exists...")
|
|
111
|
+
|
|
112
|
+
adhoc_kb_response = table_api_call(
|
|
113
|
+
instance=self._base_initial_instance, # admin permissions to contribute to the KB
|
|
114
|
+
table="kb_knowledge",
|
|
115
|
+
method="POST",
|
|
116
|
+
data=json.dumps(
|
|
117
|
+
{
|
|
118
|
+
"short_description": article_name,
|
|
119
|
+
"sys_class_name": "kb_knowledge",
|
|
120
|
+
"text": article_text,
|
|
121
|
+
"article_type": "text",
|
|
122
|
+
"kb_knowledge_base": self.adhoc_kb_sys_id,
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
)["result"]
|
|
126
|
+
|
|
127
|
+
return adhoc_kb_response
|
|
128
|
+
|
|
129
|
+
def setup_goal(self, page: Page) -> tuple[str, dict]:
|
|
130
|
+
# Create the KB
|
|
131
|
+
self.adhoc_kb_sys_id = self.create_adhoc_kb()
|
|
132
|
+
# Sample a configuration
|
|
133
|
+
self.base_config = self.random.choice(self.kb_entries)
|
|
134
|
+
self.incorrect_kb_article_name = self.get_random_article_name()
|
|
135
|
+
self.correct_kb_article_name = self.get_random_article_name()
|
|
136
|
+
self.item = self.base_config["item"]
|
|
137
|
+
self.correct_answer = self.base_config["value"]
|
|
138
|
+
|
|
139
|
+
self.incorrect_answer = " ".join(
|
|
140
|
+
[fake.word() for _ in range(len(self.correct_answer.split()))]
|
|
141
|
+
) # Random incorrect answer with the same number of words as the correct answer
|
|
142
|
+
|
|
143
|
+
incorrect_kb_article = self.create_article(
|
|
144
|
+
self.incorrect_kb_article_name,
|
|
145
|
+
self.base_config["article"].replace(self.correct_answer, self.incorrect_answer),
|
|
146
|
+
)
|
|
147
|
+
self.incorrect_kb_article_sys_id = incorrect_kb_article["sys_id"]
|
|
148
|
+
self.incorrect_kb_article_number = incorrect_kb_article["number"]
|
|
149
|
+
|
|
150
|
+
correct_kb_article = self.create_article(
|
|
151
|
+
self.correct_kb_article_name, self.base_config["article"]
|
|
152
|
+
)
|
|
153
|
+
self.correct_kb_article_sys_id = correct_kb_article["sys_id"]
|
|
154
|
+
self.correct_kb_article_number = correct_kb_article["number"]
|
|
155
|
+
|
|
156
|
+
config = self.fixed_config if self.fixed_config else self._get_config()
|
|
157
|
+
|
|
158
|
+
give_kb_read_permissions(
|
|
159
|
+
self._base_initial_instance,
|
|
160
|
+
self._base_user_sysid,
|
|
161
|
+
self._base_user_name,
|
|
162
|
+
self.adhoc_kb_sys_id,
|
|
163
|
+
self.adhoc_kb_name,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if self.level == 3:
|
|
167
|
+
protocol_kb_sys_id = table_api_call(
|
|
168
|
+
instance=self._base_initial_instance,
|
|
169
|
+
table="kb_knowledge_base",
|
|
170
|
+
params={"sysparm_query": f"title={PROTOCOL_KB_NAME}"},
|
|
171
|
+
)["result"][0]["sys_id"]
|
|
172
|
+
give_kb_read_permissions(
|
|
173
|
+
self._base_initial_instance,
|
|
174
|
+
self._base_user_sysid,
|
|
175
|
+
self._base_user_name,
|
|
176
|
+
protocol_kb_sys_id,
|
|
177
|
+
PROTOCOL_KB_NAME,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Get the task description
|
|
181
|
+
self.short_description = f"Edit knowledge base article for {self.item}"
|
|
182
|
+
self.task_description = (
|
|
183
|
+
f'Referring to company protocol "{self.protocol_name}" (located in the "Company Protocols" knowledge base) edit the knowledge base to handle incorrect information. \n'
|
|
184
|
+
+ f'Searching for "{self.item}" in the knowledge base gives different articles as the output: "{self.incorrect_kb_article_name}" with number "{self.incorrect_kb_article_number}" and "{self.correct_kb_article_name}" with number "{self.correct_kb_article_number}". \n'
|
|
185
|
+
# + f'One of the articles has incorrect information "{self.incorrect_answer}" and the other one has the correct answer "{self.correct_answer}". \n'
|
|
186
|
+
+ f'The correct information for "{self.item}" should be {self.correct_answer}. '
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
goal, info = super().setup_goal(page=page, config=config)
|
|
190
|
+
|
|
191
|
+
return goal, info
|
|
192
|
+
|
|
193
|
+
def _get_config(self) -> list[tuple[AbstractServiceNowTask, dict, bool]]:
|
|
194
|
+
"""Add more extensive definition here."""
|
|
195
|
+
|
|
196
|
+
self.incorrect_config = {
|
|
197
|
+
"item": self.item,
|
|
198
|
+
"kb_article_title": self.incorrect_kb_article_name,
|
|
199
|
+
"value": self.incorrect_answer,
|
|
200
|
+
"question": self.base_config["questions"][0],
|
|
201
|
+
# "replaced_text": self.incorrect_answer,
|
|
202
|
+
"comment": f"This article has incorrect information and is obsolete. Please refer to the article numbered {self.correct_kb_article_number} for reference.",
|
|
203
|
+
"alternative_answers": [
|
|
204
|
+
self.incorrect_answer,
|
|
205
|
+
],
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
self.correct_config = {
|
|
209
|
+
"item": self.item,
|
|
210
|
+
"kb_article_title": self.correct_kb_article_name,
|
|
211
|
+
"value": self.correct_answer,
|
|
212
|
+
"question": self.base_config["questions"][0],
|
|
213
|
+
"comment": f"This article has correct information. Please DO NOT refer to the article numbered {self.incorrect_kb_article_number} for reference.",
|
|
214
|
+
"alternative_answers": [
|
|
215
|
+
self.correct_answer,
|
|
216
|
+
],
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
navigate_to_protocol_subtask = [
|
|
220
|
+
# Navigate to the KB
|
|
221
|
+
AllMenuTask(
|
|
222
|
+
instance=self.instance,
|
|
223
|
+
fixed_config={
|
|
224
|
+
"application": "Self-Service",
|
|
225
|
+
"module": "Knowledge",
|
|
226
|
+
"url": "/now/nav/ui/classic/params/target/%24knowledge.do",
|
|
227
|
+
},
|
|
228
|
+
is_validated=False,
|
|
229
|
+
used_in_level_2=False,
|
|
230
|
+
),
|
|
231
|
+
# Find the protocol for on-boarding a new user
|
|
232
|
+
KnowledgeBaseSearchTask(
|
|
233
|
+
instance=self.instance,
|
|
234
|
+
fixed_config={
|
|
235
|
+
"alternative_answers": [],
|
|
236
|
+
"item": f"{self.protocol_name}",
|
|
237
|
+
"question": 'Can you find the "Edit Knowledge Article Protocol" in the Knowledge Base?',
|
|
238
|
+
"value": "",
|
|
239
|
+
},
|
|
240
|
+
is_validated=False,
|
|
241
|
+
used_in_level_2=False,
|
|
242
|
+
),
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
search_and_comment_knowledge_base_incorrect_subtask = [
|
|
246
|
+
# Navigate to the knowledge base home page
|
|
247
|
+
AllMenuTask(
|
|
248
|
+
instance=self.instance,
|
|
249
|
+
fixed_config={
|
|
250
|
+
"application": "Self-Service",
|
|
251
|
+
"module": "Knowledge",
|
|
252
|
+
"url": "/now/nav/ui/classic/params/target/%24knowledge.do",
|
|
253
|
+
},
|
|
254
|
+
is_validated=False,
|
|
255
|
+
used_in_level_2=True,
|
|
256
|
+
),
|
|
257
|
+
KnowledgeBaseSearchTask(
|
|
258
|
+
instance=self.instance,
|
|
259
|
+
fixed_config=self.incorrect_config,
|
|
260
|
+
is_validated=False,
|
|
261
|
+
used_in_level_2=True,
|
|
262
|
+
is_correct=False,
|
|
263
|
+
is_only_navigating=True,
|
|
264
|
+
search_by_title=True,
|
|
265
|
+
),
|
|
266
|
+
# Search the knowledge base for the incorrect article
|
|
267
|
+
AddCommentToKnowledgeArticleTask(
|
|
268
|
+
instance=self.instance,
|
|
269
|
+
fixed_config=self.incorrect_config,
|
|
270
|
+
is_validated=False,
|
|
271
|
+
used_in_level_2=True,
|
|
272
|
+
),
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
search_and_comment_knowledge_base_correct_subtask = [
|
|
276
|
+
# Navigate to the knowledge base home page
|
|
277
|
+
AllMenuTask(
|
|
278
|
+
instance=self.instance,
|
|
279
|
+
fixed_config={
|
|
280
|
+
"application": "Self-Service",
|
|
281
|
+
"module": "Knowledge",
|
|
282
|
+
"url": "/now/nav/ui/classic/params/target/%24knowledge.do",
|
|
283
|
+
},
|
|
284
|
+
is_validated=False,
|
|
285
|
+
used_in_level_2=True,
|
|
286
|
+
),
|
|
287
|
+
KnowledgeBaseSearchTask(
|
|
288
|
+
instance=self.instance,
|
|
289
|
+
fixed_config=self.correct_config,
|
|
290
|
+
is_validated=False,
|
|
291
|
+
used_in_level_2=True,
|
|
292
|
+
is_only_navigating=True,
|
|
293
|
+
search_by_title=True,
|
|
294
|
+
),
|
|
295
|
+
# Search the knowledge base for the incorrect article
|
|
296
|
+
AddCommentToKnowledgeArticleTask(
|
|
297
|
+
instance=self.instance,
|
|
298
|
+
fixed_config=self.correct_config,
|
|
299
|
+
is_validated=False,
|
|
300
|
+
used_in_level_2=True,
|
|
301
|
+
),
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
config = (
|
|
305
|
+
navigate_to_protocol_subtask
|
|
306
|
+
+ search_and_comment_knowledge_base_incorrect_subtask
|
|
307
|
+
+ search_and_comment_knowledge_base_correct_subtask
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
return config
|
|
311
|
+
|
|
312
|
+
def validate(self, page: Page, chat_messages: list[str]) -> Tuple[float, bool, str, dict]:
|
|
313
|
+
incorrect_article_kb_sys_id = table_api_call(
|
|
314
|
+
instance=self.instance,
|
|
315
|
+
table="kb_knowledge",
|
|
316
|
+
params={
|
|
317
|
+
"sysparm_query": f"short_description={self.incorrect_kb_article_name}",
|
|
318
|
+
},
|
|
319
|
+
)["result"][0]["sys_id"]
|
|
320
|
+
|
|
321
|
+
incorrect_synonyms = [
|
|
322
|
+
"incorrect",
|
|
323
|
+
"wrong",
|
|
324
|
+
"false",
|
|
325
|
+
"inaccurate",
|
|
326
|
+
"mistaken",
|
|
327
|
+
"erroneous",
|
|
328
|
+
"improper",
|
|
329
|
+
"invalid",
|
|
330
|
+
"untrue",
|
|
331
|
+
"misleading",
|
|
332
|
+
"off",
|
|
333
|
+
"obsolete",
|
|
334
|
+
]
|
|
335
|
+
incorrect_validated = 0
|
|
336
|
+
all_comments = table_api_call(
|
|
337
|
+
instance=self.instance,
|
|
338
|
+
table="kb_feedback",
|
|
339
|
+
params={
|
|
340
|
+
"sysparm_query": f"article={incorrect_article_kb_sys_id}",
|
|
341
|
+
},
|
|
342
|
+
)["result"]
|
|
343
|
+
|
|
344
|
+
for comment in all_comments:
|
|
345
|
+
if (
|
|
346
|
+
any(
|
|
347
|
+
incorrect_synonym.lower() in html.unescape(comment["comments"]).lower()
|
|
348
|
+
for incorrect_synonym in incorrect_synonyms
|
|
349
|
+
)
|
|
350
|
+
and comment["sys_created_by"] == self._base_user_name
|
|
351
|
+
and self.correct_kb_article_number.lower()
|
|
352
|
+
in html.unescape(comment["comments"]).lower()
|
|
353
|
+
):
|
|
354
|
+
incorrect_validated = 1
|
|
355
|
+
break
|
|
356
|
+
|
|
357
|
+
correct_article_kb_sys_id = table_api_call(
|
|
358
|
+
instance=self.instance,
|
|
359
|
+
table="kb_knowledge",
|
|
360
|
+
params={
|
|
361
|
+
"sysparm_query": f"short_description={self.correct_kb_article_name}",
|
|
362
|
+
},
|
|
363
|
+
)["result"][0]["sys_id"]
|
|
364
|
+
|
|
365
|
+
correct_synonyms = [
|
|
366
|
+
"correct",
|
|
367
|
+
"right",
|
|
368
|
+
"accurate",
|
|
369
|
+
"true",
|
|
370
|
+
"exact",
|
|
371
|
+
"precise",
|
|
372
|
+
"proper",
|
|
373
|
+
"valid",
|
|
374
|
+
"factual",
|
|
375
|
+
"appropriate",
|
|
376
|
+
"verifiable",
|
|
377
|
+
"up-to-date",
|
|
378
|
+
"up to date",
|
|
379
|
+
]
|
|
380
|
+
correct_validated = 0
|
|
381
|
+
all_comments = table_api_call(
|
|
382
|
+
instance=self.instance,
|
|
383
|
+
table="kb_feedback",
|
|
384
|
+
params={
|
|
385
|
+
"sysparm_query": f"article={correct_article_kb_sys_id}",
|
|
386
|
+
},
|
|
387
|
+
)["result"]
|
|
388
|
+
|
|
389
|
+
for comment in all_comments:
|
|
390
|
+
if (
|
|
391
|
+
any(
|
|
392
|
+
correct_synonym.lower() in html.unescape(comment["comments"]).lower()
|
|
393
|
+
for correct_synonym in correct_synonyms
|
|
394
|
+
)
|
|
395
|
+
and comment["sys_created_by"] == self._base_user_name
|
|
396
|
+
and self.incorrect_kb_article_number.lower()
|
|
397
|
+
in html.unescape(comment["comments"]).lower()
|
|
398
|
+
):
|
|
399
|
+
correct_validated = 1
|
|
400
|
+
break
|
|
401
|
+
|
|
402
|
+
if incorrect_validated and correct_validated:
|
|
403
|
+
# Validate final_l3 tasks
|
|
404
|
+
reward, done, message, info = super().validate(page, chat_messages)
|
|
405
|
+
return reward, done, message, info
|
|
406
|
+
elif incorrect_validated and not correct_validated:
|
|
407
|
+
return (
|
|
408
|
+
0,
|
|
409
|
+
False,
|
|
410
|
+
"",
|
|
411
|
+
{
|
|
412
|
+
"message": "Comment successfully added to the incorrect article but not the correct article."
|
|
413
|
+
},
|
|
414
|
+
)
|
|
415
|
+
elif not incorrect_validated and correct_validated:
|
|
416
|
+
return (
|
|
417
|
+
0,
|
|
418
|
+
False,
|
|
419
|
+
"",
|
|
420
|
+
{
|
|
421
|
+
"message": "Comment successfully added to the correct article but not the incorrect article."
|
|
422
|
+
},
|
|
423
|
+
)
|
|
424
|
+
else:
|
|
425
|
+
return (
|
|
426
|
+
0,
|
|
427
|
+
False,
|
|
428
|
+
"",
|
|
429
|
+
{
|
|
430
|
+
"message": "Comment not added to either the correct article or the incorrect article."
|
|
431
|
+
},
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def teardown(self) -> None:
|
|
435
|
+
# Delete created articles
|
|
436
|
+
table_api_call(
|
|
437
|
+
instance=self._base_initial_instance,
|
|
438
|
+
table=f"kb_knowledge/{self.incorrect_kb_article_sys_id}",
|
|
439
|
+
method="DELETE",
|
|
440
|
+
)
|
|
441
|
+
table_api_call(
|
|
442
|
+
instance=self._base_initial_instance,
|
|
443
|
+
table=f"kb_knowledge/{self.correct_kb_article_sys_id}",
|
|
444
|
+
method="DELETE",
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Archive knowledge base
|
|
448
|
+
table_api_call(
|
|
449
|
+
instance=self._base_initial_instance,
|
|
450
|
+
table=f"kb_knowledge_base/{self.adhoc_kb_sys_id}",
|
|
451
|
+
method="PATCH",
|
|
452
|
+
json={"title": f"archived_{self.adhoc_kb_sys_id}", "active": "false"},
|
|
453
|
+
)
|
|
454
|
+
return super().teardown()
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
__TASKS__ = [EditKnowledgeBaseTask]
|