clerk-sdk 0.2.5__py3-none-any.whl → 0.2.6__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.
@@ -11,6 +11,7 @@ from clerk.gui_automation.ui_state_inspector.models import (
11
11
  TargetWithAnchor,
12
12
  )
13
13
  from clerk.models.remote_device import RemoteDevice
14
+ from clerk.models.ui_operator import UiOperatorTask
14
15
 
15
16
 
16
17
  class RPAClerk(BaseClerk):
@@ -46,6 +47,16 @@ class RPAClerk(BaseClerk):
46
47
  raise RuntimeError("No coordinates found in the response.")
47
48
  return Coords(**res.data[0])
48
49
 
50
+ def create_ui_operator_task(self, payload: Dict) -> UiOperatorTask:
51
+ endpoint = "/ui_operator"
52
+ res = self.post_request(endpoint=endpoint, json=payload)
53
+ return UiOperatorTask(**res.data[0])
54
+
55
+ def get_ui_operator_task(self, id: str) -> UiOperatorTask:
56
+ endpoint = "/ui_operator"
57
+ res = self.get_request(endpoint=endpoint, params={"task_id": id})
58
+ return UiOperatorTask(**res.data[0])
59
+
49
60
 
50
61
  class GUIVisionClerk(BaseClerk):
51
62
  root_endpoint: str = "/gui_automation/vision"
@@ -66,7 +66,7 @@ async def _get_screen_async() -> str:
66
66
  and returns the base64 encoded image of the screen captured.
67
67
  """
68
68
  payload = {
69
- "proc_inst_id": os.getenv("PROC_ID"),
69
+ "proc_inst_id": os.getenv("_run_id"),
70
70
  "client_name": os.getenv("REMOTE_DEVICE_NAME"),
71
71
  "headless": True,
72
72
  "action": {"action_type": "screenshot"},
@@ -132,7 +132,7 @@ async def _perform_action_async(
132
132
  PerformActionException: If the action fails with an error message.
133
133
  """
134
134
  req_payload: Dict = {
135
- "proc_inst_id": os.getenv("PROC_ID"),
135
+ "proc_inst_id": os.getenv("_run_id"),
136
136
  "client_name": os.getenv("REMOTE_DEVICE_NAME"),
137
137
  "headless": (
138
138
  True if os.getenv("HEADLESS", "True").lower() == "true" else False
@@ -95,7 +95,8 @@ def gui_automation(
95
95
  def wrapper(payload: ClerkCodePayload, *args, **kwargs):
96
96
  global global_ws
97
97
  force_deallocate = False
98
- os.environ["PROC_ID"] = payload.run_id
98
+ os.environ["_document_id"] = payload.document.id
99
+ os.environ["_run_id"] = payload.run_id
99
100
 
100
101
  remote_device = _allocate_remote_device(
101
102
  clerk_client, group_name, payload.run_id
@@ -122,12 +123,17 @@ def gui_automation(
122
123
  raise WebSocketConnectionFailed()
123
124
 
124
125
  except Exception as e:
125
- os.environ.pop("PROC_ID", None)
126
126
  force_deallocate = True
127
127
  raise
128
128
  finally:
129
+ os.environ.pop("_run_id", None)
130
+ os.environ.pop("_document_id", None)
129
131
  if not reserve_client or force_deallocate:
130
132
  _deallocate_target(clerk_client, remote_device, payload.run_id)
133
+ else:
134
+ logger.warning(
135
+ f"The client stayed reserved for the this run id: {payload.run_id}"
136
+ )
131
137
 
132
138
  if global_ws and global_ws.state is State.OPEN:
133
139
  close_task = event_loop.create_task(close_ws_connection(global_ws))
@@ -1,10 +1,16 @@
1
+ from datetime import timedelta, datetime
1
2
  import os
2
3
  import base64
4
+ import time
3
5
  from typing import Optional
4
6
  from backoff._typing import Details
5
7
 
8
+ from clerk.models.ui_operator import TaskStatuses, UiOperatorTask
6
9
  from clerk.utils.save_artifact import save_artifact
10
+ from clerk.utils import logger
7
11
  from ..client_actor import get_screen
12
+ from ..ui_actions.base import BaseAction
13
+ from ..decorators.gui_automation import clerk_client
8
14
 
9
15
 
10
16
  _MAP = {
@@ -54,6 +60,23 @@ def save_screenshot(filename: str, sub_folder: Optional[str] = None) -> str:
54
60
  )
55
61
 
56
62
 
63
+ def _format_action_string(action: BaseAction) -> str:
64
+ """
65
+ Formats action in the same format as the one used in task modules.
66
+ """
67
+ action_string = (
68
+ f"{action.__class__.__name__}(target='{action.target_name or action.target}')"
69
+ )
70
+ for anchor in action.anchors:
71
+ action_string += f".{anchor.relation}('{anchor.value}')"
72
+ if action.click_offset != [0, 0]:
73
+ action_string += (
74
+ f".offset(x={action.click_offset[0]}, y={action.click_offset[1]})"
75
+ )
76
+ action_string += ".do()"
77
+ return action_string
78
+
79
+
57
80
  def maybe_engage_operator_ui_action(details: Details) -> None:
58
81
  """
59
82
  Makes a call to the operator queue server to create an issue and waits for the allotted time for it to be resolved.
@@ -62,8 +85,40 @@ def maybe_engage_operator_ui_action(details: Details) -> None:
62
85
  :raises: The exception raised by the action if the issue is not resolved within the allotted time
63
86
  """
64
87
  # Determine if the operator should be engaged
65
- use_operator = strtobool(os.getenv("USE_OPERATOR", default="False"))
66
- if not use_operator:
88
+ ui_operator_enabled = strtobool(os.getenv("_ui_operator_enabled", default="False"))
89
+
90
+ if not ui_operator_enabled:
67
91
  raise details["exception"] # type: ignore
68
92
 
69
- raise NotImplementedError("Feature not yet implemented")
93
+ ui_operator_pooling_interval = int(os.getenv("_ui_operator_pooling_interval", "1"))
94
+ ui_operator_timeout = int(os.getenv("_ui_operator_timeout", "3600"))
95
+ resolution_deadline = datetime.now() + timedelta(seconds=ui_operator_timeout)
96
+
97
+ # Extract the action object from the details dictionary
98
+ action: BaseAction = details["args"][0]
99
+ issue_description = _format_action_string(action)
100
+
101
+ # create ui operator task
102
+ payload = {
103
+ "document_id": os.getenv("_document_id"),
104
+ "remote_device_id": os.getenv("REMOTE_DEVICE_ID"),
105
+ "issue_description": issue_description,
106
+ }
107
+ task: UiOperatorTask = clerk_client.create_ui_operator_task(payload)
108
+ while datetime.now() < resolution_deadline:
109
+ task: UiOperatorTask = clerk_client.get_ui_operator_task(task.id)
110
+ if task.status == TaskStatuses.COMPLETED:
111
+ logger.debug(
112
+ f"The ui operator task {task.id} has been resolved by {task.assignee_name}"
113
+ )
114
+ return
115
+ elif task.status == TaskStatuses.CANCELLED:
116
+ logger.warning(f"The ui operator task {task.id} has been cancelled")
117
+ raise details["exception"]
118
+
119
+ time.sleep(ui_operator_pooling_interval)
120
+
121
+ logger.warning(
122
+ f"The ui operator task {task.id} was not resolved after {ui_operator_timeout} seconds"
123
+ )
124
+ raise details["exception"]
@@ -44,6 +44,9 @@ class ScreenPilot:
44
44
  tolerate_repeat_transitions (int): Number of repeated transitions to tolerate before breaking the execution. Default 5.
45
45
  tolerate_repeat_states (int): Number of repeated states to tolerate before breaking the execution. Default 5.
46
46
  enable_force_close_app_process (bool): If true, terminates the application process via `taskkill` command. Default False.
47
+ ui_operator_enabled (bool): if true, enables the creation of ui operator task which needs to be resolved in Clerk.
48
+ ui_operator_pooling_interval (int): in seconds, defines the time pooling interval for ui operator task. Default 1.
49
+ ui_operator_timeout (int): in seconds, defines the max waiting time for the ui operator task to be resolved before raising and exception.
47
50
  process_name (Optional[str]): Name of the process that needs to be closed (ie. process.exe). Required attribute if `enable_force_close_app_process` is True
48
51
 
49
52
  Methods:
@@ -63,6 +66,9 @@ class ScreenPilot:
63
66
  tolerate_repeat_transitions: int = 5
64
67
  tolerate_repeat_states: int = 5
65
68
  enable_force_close_app_process: bool = False
69
+ ui_operator_enabled: bool = False
70
+ ui_operator_pooling_interval: int = 1 # seconds
71
+ ui_operator_timeout: int = 3600 # seconds (1 hour)
66
72
  process_name: Optional[str] = None
67
73
  _acted_since_state_eval: bool = False
68
74
  _ai_recovery_agent: Optional[CourseCorrector] = None
@@ -716,6 +722,14 @@ class ScreenPilot:
716
722
  """
717
723
  return None
718
724
 
725
+ @classmethod
726
+ def _initialize_env_variables(cls):
727
+ os.environ["_ui_operator_enabled"] = str(cls.ui_operator_enabled)
728
+ os.environ["_ui_operator_pooling_interval"] = str(
729
+ cls.ui_operator_pooling_interval
730
+ )
731
+ os.environ["_ui_operator_timeout"] = str(cls.ui_operator_timeout)
732
+
719
733
  @classmethod
720
734
  def run(
721
735
  cls, goal_function: Optional[Callable] = None, **kwargs
@@ -732,6 +746,8 @@ class ScreenPilot:
732
746
  Returns:
733
747
  - Exit reason (ScreenPilotOutcome): Exception class that interrupted the state machine execution.
734
748
  """
749
+ cls._initialize_env_variables()
750
+
735
751
  if not goal_function:
736
752
  goal_function = cls._default_goal_function
737
753
  cls._act_on_start()
@@ -0,0 +1,15 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class TaskStatuses(str, Enum):
7
+ OPEN = "open"
8
+ COMPLETED = "completed"
9
+ CANCELLED = "cancelled"
10
+
11
+
12
+ class UiOperatorTask(BaseModel):
13
+ id: str
14
+ status: TaskStatuses
15
+ assignee_name: Optional[str]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clerk-sdk
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: Library for interacting with Clerk
5
5
  Home-page: https://github.com/F-ONE-Group/clerk_pypi
6
6
  Author: F-ONE Group
@@ -5,16 +5,16 @@ clerk/decorator/__init__.py,sha256=4VCGOFNq7pA7iJS410V_R9eWfjxP7mwxwa4thwaUTf8,3
5
5
  clerk/decorator/models.py,sha256=JWFOJuUh3F40iOL0aoNgUC9GRHU3Fcq3GOjSXA7Gwng,394
6
6
  clerk/decorator/task_decorator.py,sha256=srb8AC3tNj1RWC4iNkkwkUuG33B8TtxfTpOm1Oi373Y,2169
7
7
  clerk/gui_automation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- clerk/gui_automation/client.py,sha256=LJFHdWreH0pZnRUZJ6jd4Xp3kYJjOjNLEUfHD0ly2Y0,4405
8
+ clerk/gui_automation/client.py,sha256=LqrUQtC-4pjUJYDDn_s3t073K066j_p0vf4kXLChBc0,4891
9
9
  clerk/gui_automation/action_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  clerk/gui_automation/action_model/model.py,sha256=sbTySgOq4Xv_r2p7q_ji1bGCHgRS1HXC9Z39thwCQ9c,3829
11
11
  clerk/gui_automation/action_model/utils.py,sha256=xzFxgN-bTK6HKGS7J-esQZ-ePj_yG72T-2ZVhcWvKjw,798
12
12
  clerk/gui_automation/client_actor/__init__.py,sha256=SVuL6-oo1Xc0oJkjMKrO6mJwpPGjrCLKhDV6r2Abtf8,66
13
- clerk/gui_automation/client_actor/client_actor.py,sha256=Me3NHc0nMA7Y5CUcnE0ZHT1P7dQRGR6duCAKwn0Tg6g,5081
13
+ clerk/gui_automation/client_actor/client_actor.py,sha256=YVYBgDQ89NkgdlWSk_AvDtEO_BJe5YIHU5OJK4cUs28,5081
14
14
  clerk/gui_automation/client_actor/exception.py,sha256=zdnImHZ88yf52Xq3aMHivEU3aJg-r2c-r8x8XZnI3ic,407
15
15
  clerk/gui_automation/client_actor/model.py,sha256=wVpFCi1w2kh4kAV8oNx489vf_SLUQnqhc02rFD5NIJA,6335
16
16
  clerk/gui_automation/decorators/__init__.py,sha256=OCgXStEumscgT-RyVy5OKS7ml1w9y-lEnjCVnxuRnQs,43
17
- clerk/gui_automation/decorators/gui_automation.py,sha256=0p83i6bu6f2syxfOy96oRnNMVc2ZBKYfNJIE1sE1xYA,5064
17
+ clerk/gui_automation/decorators/gui_automation.py,sha256=7p3xU2biyHN43hgEAGj34QJjHF2SeBDM6FRCx_eqewM,5350
18
18
  clerk/gui_automation/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  clerk/gui_automation/exceptions/agent_manager.py,sha256=KFWFWQ7X_8Z9XG__SMzb1jKI3yNoVTPJAXzW5O-fSXc,101
20
20
  clerk/gui_automation/exceptions/websocket.py,sha256=-MdwSwlf1hbnu55aDgk3L1znkTZ6xJS6po5VpEGD9ck,117
@@ -23,7 +23,7 @@ clerk/gui_automation/exceptions/modality/exc.py,sha256=P-dMuCTyVZYD3pbGpCf_1SYEg
23
23
  clerk/gui_automation/ui_actions/__init__.py,sha256=-EDQ5375HXrvG3sfFY7zOPC405YcBL6xXRACm2p-YyI,23
24
24
  clerk/gui_automation/ui_actions/actions.py,sha256=hhxl5VMDNSXdqm2L0tZqs6IhJHVXtlSVSdwsiz2BbDI,27449
25
25
  clerk/gui_automation/ui_actions/base.py,sha256=4CjOkMZNLt8W0tLO91tOkz8CCea0m2IRBIOpWNMOZUA,7230
26
- clerk/gui_automation/ui_actions/support.py,sha256=BS9QReyP_F9-oGPt0fuy5BD9kC9-E5mOE05Zk0M1Hzw,2176
26
+ clerk/gui_automation/ui_actions/support.py,sha256=83D-oVmZxD507kh9-8PsoXt5zD4o225TmkmS0Kh_8Zo,4330
27
27
  clerk/gui_automation/ui_state_inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  clerk/gui_automation/ui_state_inspector/gui_vision.py,sha256=lRnF3IMLViOB-UE447A4kc8BO7NKqqLV1NZKRswRKWg,7707
29
29
  clerk/gui_automation/ui_state_inspector/models.py,sha256=uuzYhE9is4Z-TmsVMA1mwf0mQGs_PDLqdSZKbe8dHSs,5691
@@ -32,18 +32,19 @@ clerk/gui_automation/ui_state_machine/ai_recovery.py,sha256=3_Gu_RPxta4eXmXWH3WA
32
32
  clerk/gui_automation/ui_state_machine/decorators.py,sha256=gBOpIusjsXlA7FEiszDCFKTS6vXx3LBNMz_SQJNkMWg,2134
33
33
  clerk/gui_automation/ui_state_machine/exceptions.py,sha256=9KWg20dnCssMdMu632E0nP5vkndgYNI4cDCDW-vMkQA,1436
34
34
  clerk/gui_automation/ui_state_machine/models.py,sha256=1fpNYPsff0dyLg1wNJl-xQyMYvy4cSDy4OLxa0ej-0U,1395
35
- clerk/gui_automation/ui_state_machine/state_machine.py,sha256=6WJQ5NsI9hOqN-VM0lYf_mv-xOQd0UjolGXJ0M58L1I,35230
35
+ clerk/gui_automation/ui_state_machine/state_machine.py,sha256=2OfN17N_8dzhul2X19koqGWDZbgjeJu7ExTLpVzf1OA,36130
36
36
  clerk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  clerk/models/document.py,sha256=Bbt3YSvGcOrJJjJBYjbDGXpZIaQAYdq_-uIr2urwy5s,525
38
38
  clerk/models/document_statuses.py,sha256=ytTQhgACs2m22qz51_7Ti0IxzbVyl-fl7uF7CnDEyLA,279
39
39
  clerk/models/file.py,sha256=7n6bV6ukRA-uh7MFxUx_TEMNpVAdNe5O0nKIeneCQhg,459
40
40
  clerk/models/remote_device.py,sha256=2jijS-9WWq7y6xIbAaDaPnzZo3-tjp2-dCQvNKP8YSU,109
41
41
  clerk/models/response_model.py,sha256=R62daUN1YVOwgnrh_epvFRsQcOwT7R4u97l73egvm-c,232
42
+ clerk/models/ui_operator.py,sha256=mKTJUFZgv7PeEt5oys28HVZxHOJsofmRQOcRpqj0dbU,293
42
43
  clerk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
44
  clerk/utils/logger.py,sha256=_jYOSEc1WCsY9DvU8cIWUPmz41QNjpHTEZ7WrUnmFPA,3650
44
45
  clerk/utils/save_artifact.py,sha256=Lwl3l3QqKnSFLFpfEEa-dJ-dV9OxvCU3ADkuYZ18U-Q,1117
45
- clerk_sdk-0.2.5.dist-info/licenses/LICENSE,sha256=GTVQl3vH6ht70wJXKC0yMT8CmXKHxv_YyO_utAgm7EA,1065
46
- clerk_sdk-0.2.5.dist-info/METADATA,sha256=5X609M2WJHnk6rKf0diLFtkS42gDhJ4zi5ZCYMPsxXY,3508
47
- clerk_sdk-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
- clerk_sdk-0.2.5.dist-info/top_level.txt,sha256=99eQiU6d05_-f41tmSFanfI_SIJeAdh7u9m3LNSfcv4,6
49
- clerk_sdk-0.2.5.dist-info/RECORD,,
46
+ clerk_sdk-0.2.6.dist-info/licenses/LICENSE,sha256=GTVQl3vH6ht70wJXKC0yMT8CmXKHxv_YyO_utAgm7EA,1065
47
+ clerk_sdk-0.2.6.dist-info/METADATA,sha256=mC8t5huCNbfWuHZ2dy4O3d0-XMHx8yy8n1cFCgLbhzk,3508
48
+ clerk_sdk-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ clerk_sdk-0.2.6.dist-info/top_level.txt,sha256=99eQiU6d05_-f41tmSFanfI_SIJeAdh7u9m3LNSfcv4,6
50
+ clerk_sdk-0.2.6.dist-info/RECORD,,