clerk-sdk 0.2.0__py3-none-any.whl → 0.2.2__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.
- clerk/gui_automation/client.py +4 -0
- clerk/gui_automation/client_actor/client_actor.py +1 -3
- clerk/gui_automation/decorators/gui_automation.py +35 -9
- clerk/gui_automation/exceptions/agent_manager.py +6 -0
- clerk/gui_automation/ui_actions/support.py +2 -1
- clerk/utils/logger.py +7 -1
- clerk/utils/save_artifact.py +2 -0
- {clerk_sdk-0.2.0.dist-info → clerk_sdk-0.2.2.dist-info}/METADATA +1 -1
- {clerk_sdk-0.2.0.dist-info → clerk_sdk-0.2.2.dist-info}/RECORD +12 -11
- {clerk_sdk-0.2.0.dist-info → clerk_sdk-0.2.2.dist-info}/WHEEL +0 -0
- {clerk_sdk-0.2.0.dist-info → clerk_sdk-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {clerk_sdk-0.2.0.dist-info → clerk_sdk-0.2.2.dist-info}/top_level.txt +0 -0
clerk/gui_automation/client.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
from clerk.base import BaseClerk
|
|
5
5
|
from clerk.gui_automation.action_model.model import Coords
|
|
6
|
+
from clerk.gui_automation.exceptions.agent_manager import NoClientsAvailable
|
|
6
7
|
from clerk.gui_automation.ui_state_inspector.models import (
|
|
7
8
|
ActionString,
|
|
8
9
|
BaseState,
|
|
@@ -22,6 +23,9 @@ class RPAClerk(BaseClerk):
|
|
|
22
23
|
endpoint=endpoint, json={"group_name": group_name, "run_id": run_id}
|
|
23
24
|
)
|
|
24
25
|
|
|
26
|
+
if res.data[0] is None:
|
|
27
|
+
raise NoClientsAvailable()
|
|
28
|
+
|
|
25
29
|
return RemoteDevice(**res.data[0])
|
|
26
30
|
|
|
27
31
|
def deallocate_remote_device(
|
|
@@ -68,9 +68,7 @@ async def _get_screen_async() -> str:
|
|
|
68
68
|
payload = {
|
|
69
69
|
"proc_inst_id": os.getenv("PROC_ID"),
|
|
70
70
|
"client_name": os.getenv("REMOTE_DEVICE_NAME"),
|
|
71
|
-
"headless":
|
|
72
|
-
True if os.getenv("HEADLESS", "True").lower() == "true" else False
|
|
73
|
-
),
|
|
71
|
+
"headless": True,
|
|
74
72
|
"action": {"action_type": "screenshot"},
|
|
75
73
|
}
|
|
76
74
|
try:
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import functools
|
|
3
|
-
import logging
|
|
4
3
|
import os
|
|
4
|
+
import time
|
|
5
5
|
from typing import Callable, Union
|
|
6
6
|
|
|
7
7
|
from websockets.asyncio.client import connect, ClientConnection
|
|
8
8
|
from websockets.protocol import State
|
|
9
9
|
|
|
10
10
|
from clerk.gui_automation.client import RPAClerk
|
|
11
|
+
from clerk.gui_automation.exceptions.agent_manager import (
|
|
12
|
+
ClientAvailabilityTimeout,
|
|
13
|
+
NoClientsAvailable,
|
|
14
|
+
)
|
|
11
15
|
from clerk.models.remote_device import RemoteDevice
|
|
12
16
|
from clerk.decorator.models import ClerkCodePayload
|
|
17
|
+
from clerk.utils import logger
|
|
13
18
|
from ..exceptions.websocket import WebSocketConnectionFailed
|
|
14
19
|
|
|
15
20
|
|
|
@@ -19,22 +24,43 @@ global_ws: Union[ClientConnection, None] = None
|
|
|
19
24
|
clerk_client = RPAClerk()
|
|
20
25
|
wss_uri = "wss://agent-manager.f-one.group/action"
|
|
21
26
|
|
|
27
|
+
CLIENT_TIMEOUT_MINS = 1
|
|
28
|
+
MAX_CLIENT_AVAILABILITY_RETRIES = 60
|
|
29
|
+
|
|
22
30
|
|
|
23
31
|
def _allocate_remote_device(
|
|
24
32
|
clerk_client: RPAClerk, group_name: str, run_id: str
|
|
25
33
|
) -> RemoteDevice:
|
|
26
|
-
remote_device =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
remote_device = None
|
|
35
|
+
retries = 0
|
|
36
|
+
|
|
37
|
+
while True:
|
|
38
|
+
try:
|
|
39
|
+
remote_device = clerk_client.allocate_remote_device(
|
|
40
|
+
group_name=group_name, run_id=run_id
|
|
41
|
+
)
|
|
42
|
+
os.environ["REMOTE_DEVICE_ID"] = remote_device.id
|
|
43
|
+
os.environ["REMOTE_DEVICE_NAME"] = remote_device.name
|
|
44
|
+
logger.debug(f"Remote device allocated: {remote_device.name}")
|
|
45
|
+
return remote_device
|
|
46
|
+
|
|
47
|
+
except NoClientsAvailable:
|
|
48
|
+
logger.warning(
|
|
49
|
+
f"No clients are available for {group_name} group. Initiating a {CLIENT_TIMEOUT_MINS} minute wait. Retry count: {retries}"
|
|
50
|
+
)
|
|
51
|
+
retries += 1
|
|
52
|
+
if retries == MAX_CLIENT_AVAILABILITY_RETRIES:
|
|
53
|
+
raise ClientAvailabilityTimeout(
|
|
54
|
+
f"No clients available for {group_name} group after {CLIENT_TIMEOUT_MINS*MAX_CLIENT_AVAILABILITY_RETRIES} minutes"
|
|
55
|
+
)
|
|
56
|
+
time.sleep(CLIENT_TIMEOUT_MINS * 60)
|
|
32
57
|
|
|
33
58
|
|
|
34
59
|
def _deallocate_target(
|
|
35
60
|
clerk_client: RPAClerk, remote_device: RemoteDevice, run_id: str
|
|
36
61
|
):
|
|
37
62
|
clerk_client.deallocate_remote_device(remote_device=remote_device, run_id=run_id)
|
|
63
|
+
logger.debug("Remote device deallocated")
|
|
38
64
|
os.environ.pop("REMOTE_DEVICE_ID", None)
|
|
39
65
|
os.environ.pop("REMOTE_DEVICE_NAME", None)
|
|
40
66
|
|
|
@@ -82,7 +108,7 @@ def gui_automation():
|
|
|
82
108
|
global_ws = event_loop.run_until_complete(task)
|
|
83
109
|
|
|
84
110
|
if global_ws and global_ws.state is State.OPEN:
|
|
85
|
-
|
|
111
|
+
logger.debug("WebSocket connection established.")
|
|
86
112
|
func_ret = func(payload, *args, **kwargs)
|
|
87
113
|
else:
|
|
88
114
|
global_ws = None
|
|
@@ -97,7 +123,7 @@ def gui_automation():
|
|
|
97
123
|
if global_ws and global_ws.state is State.OPEN:
|
|
98
124
|
close_task = event_loop.create_task(close_ws_connection(global_ws))
|
|
99
125
|
event_loop.run_until_complete(close_task)
|
|
100
|
-
|
|
126
|
+
logger.debug("WebSocket connection closed.")
|
|
101
127
|
|
|
102
128
|
event_loop.run_until_complete(event_loop.shutdown_asyncgens())
|
|
103
129
|
event_loop.close()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import base64
|
|
2
3
|
from typing import Optional
|
|
3
4
|
from backoff._typing import Details
|
|
4
5
|
|
|
@@ -48,7 +49,7 @@ def save_screenshot(filename: str, sub_folder: Optional[str] = None) -> str:
|
|
|
48
49
|
screen_b64: str = get_screen()
|
|
49
50
|
return save_artifact(
|
|
50
51
|
filename=filename,
|
|
51
|
-
file_bytes=
|
|
52
|
+
file_bytes=base64.b64decode(screen_b64),
|
|
52
53
|
subfolder=sub_folder,
|
|
53
54
|
)
|
|
54
55
|
|
clerk/utils/logger.py
CHANGED
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
if sys.platform == "win32":
|
|
7
|
-
base_path = os.path.join(os.getcwd(), "data", "
|
|
7
|
+
base_path = os.path.join(os.getcwd(), "data", "artifacts")
|
|
8
8
|
else:
|
|
9
9
|
base_path = "/app/data/artifacts"
|
|
10
10
|
|
|
@@ -100,7 +100,13 @@ def _log(level: str, message: str):
|
|
|
100
100
|
formatter = logging.Formatter(format)
|
|
101
101
|
fh.setFormatter(formatter)
|
|
102
102
|
logger.addHandler(fh)
|
|
103
|
+
# → console handler (same formatter)
|
|
104
|
+
sh = logging.StreamHandler(sys.stdout)
|
|
105
|
+
sh.setFormatter(formatter)
|
|
106
|
+
logger.addHandler(sh)
|
|
107
|
+
|
|
103
108
|
logger.setLevel(logging.DEBUG)
|
|
109
|
+
logger.propagate = False # keep root logger from printing duplicates
|
|
104
110
|
|
|
105
111
|
# Log the message based on the level
|
|
106
112
|
if level.lower() == "info":
|
clerk/utils/save_artifact.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Optional
|
|
3
|
+
from clerk.utils import logger
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
def save_artifact(
|
|
@@ -32,4 +33,5 @@ def save_artifact(
|
|
|
32
33
|
with open(file_path, "wb") as f:
|
|
33
34
|
f.write(file_bytes)
|
|
34
35
|
|
|
36
|
+
logger.debug(f"Artifact successfully saved at: {file_path}")
|
|
35
37
|
return file_path
|
|
@@ -5,24 +5,25 @@ 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=
|
|
8
|
+
clerk/gui_automation/client.py,sha256=LJFHdWreH0pZnRUZJ6jd4Xp3kYJjOjNLEUfHD0ly2Y0,4405
|
|
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=
|
|
13
|
+
clerk/gui_automation/client_actor/client_actor.py,sha256=Me3NHc0nMA7Y5CUcnE0ZHT1P7dQRGR6duCAKwn0Tg6g,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=
|
|
17
|
+
clerk/gui_automation/decorators/gui_automation.py,sha256=C7s1HekmLiXvw3I7O3oCrrTAAlxb_w3PA7YAILGhSLc,4711
|
|
18
18
|
clerk/gui_automation/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
clerk/gui_automation/exceptions/agent_manager.py,sha256=KFWFWQ7X_8Z9XG__SMzb1jKI3yNoVTPJAXzW5O-fSXc,101
|
|
19
20
|
clerk/gui_automation/exceptions/websocket.py,sha256=-MdwSwlf1hbnu55aDgk3L1znkTZ6xJS6po5VpEGD9ck,117
|
|
20
21
|
clerk/gui_automation/exceptions/modality/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
22
|
clerk/gui_automation/exceptions/modality/exc.py,sha256=P-dMuCTyVZYD3pbGpCf_1SYEgaETn13c51pmfbsXr5k,1436
|
|
22
23
|
clerk/gui_automation/ui_actions/__init__.py,sha256=-EDQ5375HXrvG3sfFY7zOPC405YcBL6xXRACm2p-YyI,23
|
|
23
24
|
clerk/gui_automation/ui_actions/actions.py,sha256=hhxl5VMDNSXdqm2L0tZqs6IhJHVXtlSVSdwsiz2BbDI,27449
|
|
24
25
|
clerk/gui_automation/ui_actions/base.py,sha256=4CjOkMZNLt8W0tLO91tOkz8CCea0m2IRBIOpWNMOZUA,7230
|
|
25
|
-
clerk/gui_automation/ui_actions/support.py,sha256=
|
|
26
|
+
clerk/gui_automation/ui_actions/support.py,sha256=BS9QReyP_F9-oGPt0fuy5BD9kC9-E5mOE05Zk0M1Hzw,2176
|
|
26
27
|
clerk/gui_automation/ui_state_inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
28
|
clerk/gui_automation/ui_state_inspector/gui_vision.py,sha256=lRnF3IMLViOB-UE447A4kc8BO7NKqqLV1NZKRswRKWg,7707
|
|
28
29
|
clerk/gui_automation/ui_state_inspector/models.py,sha256=uuzYhE9is4Z-TmsVMA1mwf0mQGs_PDLqdSZKbe8dHSs,5691
|
|
@@ -39,10 +40,10 @@ clerk/models/file.py,sha256=7n6bV6ukRA-uh7MFxUx_TEMNpVAdNe5O0nKIeneCQhg,459
|
|
|
39
40
|
clerk/models/remote_device.py,sha256=2jijS-9WWq7y6xIbAaDaPnzZo3-tjp2-dCQvNKP8YSU,109
|
|
40
41
|
clerk/models/response_model.py,sha256=R62daUN1YVOwgnrh_epvFRsQcOwT7R4u97l73egvm-c,232
|
|
41
42
|
clerk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
-
clerk/utils/logger.py,sha256=
|
|
43
|
-
clerk/utils/save_artifact.py,sha256=
|
|
44
|
-
clerk_sdk-0.2.
|
|
45
|
-
clerk_sdk-0.2.
|
|
46
|
-
clerk_sdk-0.2.
|
|
47
|
-
clerk_sdk-0.2.
|
|
48
|
-
clerk_sdk-0.2.
|
|
43
|
+
clerk/utils/logger.py,sha256=_jYOSEc1WCsY9DvU8cIWUPmz41QNjpHTEZ7WrUnmFPA,3650
|
|
44
|
+
clerk/utils/save_artifact.py,sha256=Lwl3l3QqKnSFLFpfEEa-dJ-dV9OxvCU3ADkuYZ18U-Q,1117
|
|
45
|
+
clerk_sdk-0.2.2.dist-info/licenses/LICENSE,sha256=GTVQl3vH6ht70wJXKC0yMT8CmXKHxv_YyO_utAgm7EA,1065
|
|
46
|
+
clerk_sdk-0.2.2.dist-info/METADATA,sha256=8VZysSvY_q_eoLR6DQ7MetvLforSTw82vDUbGTAXfJI,3508
|
|
47
|
+
clerk_sdk-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
48
|
+
clerk_sdk-0.2.2.dist-info/top_level.txt,sha256=99eQiU6d05_-f41tmSFanfI_SIJeAdh7u9m3LNSfcv4,6
|
|
49
|
+
clerk_sdk-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|