clerk-sdk 0.1.8__py3-none-any.whl → 0.2.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.
- clerk/base.py +94 -0
- clerk/client.py +3 -104
- clerk/decorator/models.py +1 -0
- clerk/decorator/task_decorator.py +4 -1
- clerk/gui_automation/__init__.py +0 -0
- clerk/gui_automation/action_model/__init__.py +0 -0
- clerk/gui_automation/action_model/model.py +126 -0
- clerk/gui_automation/action_model/utils.py +26 -0
- clerk/gui_automation/client.py +144 -0
- clerk/gui_automation/client_actor/__init__.py +4 -0
- clerk/gui_automation/client_actor/client_actor.py +178 -0
- clerk/gui_automation/client_actor/exception.py +22 -0
- clerk/gui_automation/client_actor/model.py +192 -0
- clerk/gui_automation/decorators/__init__.py +1 -0
- clerk/gui_automation/decorators/gui_automation.py +109 -0
- clerk/gui_automation/exceptions/__init__.py +0 -0
- clerk/gui_automation/exceptions/modality/__init__.py +0 -0
- clerk/gui_automation/exceptions/modality/exc.py +46 -0
- clerk/gui_automation/exceptions/websocket.py +6 -0
- clerk/gui_automation/ui_actions/__init__.py +1 -0
- clerk/gui_automation/ui_actions/actions.py +781 -0
- clerk/gui_automation/ui_actions/base.py +200 -0
- clerk/gui_automation/ui_actions/support.py +68 -0
- clerk/gui_automation/ui_state_inspector/__init__.py +0 -0
- clerk/gui_automation/ui_state_inspector/gui_vision.py +184 -0
- clerk/gui_automation/ui_state_inspector/models.py +184 -0
- clerk/gui_automation/ui_state_machine/__init__.py +11 -0
- clerk/gui_automation/ui_state_machine/ai_recovery.py +110 -0
- clerk/gui_automation/ui_state_machine/decorators.py +71 -0
- clerk/gui_automation/ui_state_machine/exceptions.py +42 -0
- clerk/gui_automation/ui_state_machine/models.py +40 -0
- clerk/gui_automation/ui_state_machine/state_machine.py +838 -0
- clerk/models/remote_device.py +7 -0
- clerk/utils/__init__.py +0 -0
- clerk/utils/logger.py +118 -0
- clerk/utils/save_artifact.py +35 -0
- {clerk_sdk-0.1.8.dist-info → clerk_sdk-0.2.0.dist-info}/METADATA +11 -1
- clerk_sdk-0.2.0.dist-info/RECORD +48 -0
- clerk_sdk-0.1.8.dist-info/RECORD +0 -15
- {clerk_sdk-0.1.8.dist-info → clerk_sdk-0.2.0.dist-info}/WHEEL +0 -0
- {clerk_sdk-0.1.8.dist-info → clerk_sdk-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {clerk_sdk-0.1.8.dist-info → clerk_sdk-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from typing import Union, List, Optional, Type
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
from clerk.gui_automation.client import CourseCorrectorClerk
|
|
5
|
+
|
|
6
|
+
from ..client_actor.client_actor import get_screen
|
|
7
|
+
from .models import ActionString
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CourseCorrector(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Interface for a CourseCorrector class that can generate corrective actions based on a goal and feedback.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
goal: str
|
|
17
|
+
custom_instructions: Optional[str] = None
|
|
18
|
+
|
|
19
|
+
def get_corrective_actions(
|
|
20
|
+
self, output_model: Type[ActionString] = ActionString
|
|
21
|
+
) -> List[ActionString]:
|
|
22
|
+
"""
|
|
23
|
+
Writes an action string based on the provided prompt.
|
|
24
|
+
Args:
|
|
25
|
+
action_prompt: The prompt for the action to write.
|
|
26
|
+
output_model: The model to use for the response.
|
|
27
|
+
Returns:
|
|
28
|
+
List of ActionString models.
|
|
29
|
+
"""
|
|
30
|
+
raise NotImplementedError("get_corrective_action method is not implemented")
|
|
31
|
+
|
|
32
|
+
def add_feedback(self, feedback: str) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Adds feedback to the CourseCorrector instance.
|
|
35
|
+
Args:
|
|
36
|
+
feedback: The feedback to add.
|
|
37
|
+
"""
|
|
38
|
+
raise NotImplementedError("add_feedback method is not implemented")
|
|
39
|
+
|
|
40
|
+
def get_latest_feedback(self) -> Optional[str]:
|
|
41
|
+
"""
|
|
42
|
+
Gets the latest feedback added to the CourseCorrector instance.
|
|
43
|
+
Returns:
|
|
44
|
+
The latest feedback as a string, or None if no feedback has been added.
|
|
45
|
+
"""
|
|
46
|
+
raise NotImplementedError("get_latest_feedback method is not implemented")
|
|
47
|
+
|
|
48
|
+
def reset_feedback(self) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Resets the latest feedback added to the CourseCorrector instance.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError("reset_feedback method is not implemented")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CourseCorrectorV1(CourseCorrector):
|
|
56
|
+
name: str = "CourseCorrectorV1"
|
|
57
|
+
use_ocr: bool = Field(
|
|
58
|
+
default=True,
|
|
59
|
+
description="Whether OCR of the screen should be included with in the model call (increases precision with "
|
|
60
|
+
"small details).",
|
|
61
|
+
)
|
|
62
|
+
clerk_client: CourseCorrectorClerk = CourseCorrectorClerk()
|
|
63
|
+
|
|
64
|
+
def get_corrective_actions(
|
|
65
|
+
self,
|
|
66
|
+
output_model: Type[ActionString] = ActionString,
|
|
67
|
+
) -> List[ActionString]:
|
|
68
|
+
"""
|
|
69
|
+
Writes an action string based on the provided prompt.
|
|
70
|
+
Args:
|
|
71
|
+
action_prompt: The prompt for the action to write.
|
|
72
|
+
output_model: The model to use for the response.
|
|
73
|
+
Returns:
|
|
74
|
+
List of ActionString models.
|
|
75
|
+
"""
|
|
76
|
+
screen_b64 = get_screen()
|
|
77
|
+
action_string = self.clerk_client.get_corrective_actions(
|
|
78
|
+
screen_b64, self.use_ocr, self.goal, self.custom_instructions
|
|
79
|
+
)
|
|
80
|
+
assert isinstance(action_string, ActionString)
|
|
81
|
+
return [action_string]
|
|
82
|
+
|
|
83
|
+
def add_feedback(self, feedback: str) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Adds feedback to the CourseCorrector instance.
|
|
86
|
+
Args:
|
|
87
|
+
feedback: The feedback to add.
|
|
88
|
+
"""
|
|
89
|
+
self.latest_feedback = feedback
|
|
90
|
+
|
|
91
|
+
def get_latest_feedback(self) -> Optional[str]:
|
|
92
|
+
"""
|
|
93
|
+
Gets the latest feedback added to the CourseCorrector instance.
|
|
94
|
+
Returns:
|
|
95
|
+
The latest feedback as a string, or None if no feedback has been added.
|
|
96
|
+
"""
|
|
97
|
+
return self.latest_feedback
|
|
98
|
+
|
|
99
|
+
def reset_feedback(self) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Resets the latest feedback added to the CourseCorrector instance.
|
|
102
|
+
"""
|
|
103
|
+
self.latest_feedback = None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def course_corrector_v1(
|
|
107
|
+
goal: str, custom_instructions: Union[str, None] = None
|
|
108
|
+
) -> CourseCorrectorV1:
|
|
109
|
+
"""Factory function for generating a CourseCorrector instance with the specified goal."""
|
|
110
|
+
return CourseCorrectorV1(goal=goal, custom_instructions=custom_instructions)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Callable, Optional
|
|
2
|
+
from .state_machine import ScreenPilot
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def state(*args, start_allowed=True, end_allowed=True):
|
|
6
|
+
"""
|
|
7
|
+
Register a state class with the ScreenPilot state machine.
|
|
8
|
+
|
|
9
|
+
Parameters:
|
|
10
|
+
- start_allowed (bool): Whether the process may start from this state.
|
|
11
|
+
- end_allowed (bool): Whether the process may end in this state.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
- None
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
@state(start_allowed=True, end_allowed=False)
|
|
18
|
+
"""
|
|
19
|
+
if args and callable(args[0]):
|
|
20
|
+
# It's used as @state without arguments, args[0] is the class
|
|
21
|
+
cls = args[0]
|
|
22
|
+
return state()(cls) # Apply default arguments
|
|
23
|
+
else:
|
|
24
|
+
|
|
25
|
+
def class_decorator(cls):
|
|
26
|
+
ScreenPilot.register_state(
|
|
27
|
+
cls, start_allowed=start_allowed, end_allowed=end_allowed
|
|
28
|
+
)
|
|
29
|
+
return cls
|
|
30
|
+
|
|
31
|
+
return class_decorator
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def transition(from_state: str, to_state: str, condition: Optional[Callable] = None):
|
|
35
|
+
"""
|
|
36
|
+
Register a transition between two states with the ScreenPilot state machine.
|
|
37
|
+
|
|
38
|
+
Parameters:
|
|
39
|
+
- from_state (str): The name of the state to transition from.
|
|
40
|
+
- to_state (str): The name of the state to transition to.
|
|
41
|
+
- condition (Callable): Condition to disambiguate between multiple transitions. Must return a boolean.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
- None
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
@transition('StateA', 'StateB')
|
|
48
|
+
"""
|
|
49
|
+
return ScreenPilot.register_transition(
|
|
50
|
+
from_state, to_state, mode="planned", condition=condition
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def rollback(from_state: str, to_state: str, condition: Optional[Callable] = None):
|
|
55
|
+
"""
|
|
56
|
+
Register a rollback transition between two states with the ScreenPilot state machine.
|
|
57
|
+
|
|
58
|
+
Parameters:
|
|
59
|
+
- from_state (str): The name of the state to transition from.
|
|
60
|
+
- to_state (str): The name of the state to transition to.
|
|
61
|
+
- condition (Callable): Condition to disambiguate between multiple rollbacks. Must return a boolean.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
- None
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
@rollback('StateA', 'StateB')
|
|
68
|
+
"""
|
|
69
|
+
return ScreenPilot.register_transition(
|
|
70
|
+
from_state, to_state, mode="rollback", condition=condition
|
|
71
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ScreenPilotOutcome(Exception):
|
|
5
|
+
"""Base class for all exceptions and conditions raised by the ScreenPilot state machine."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ScreenPilotException(Exception):
|
|
9
|
+
"""Base class for all exceptions raised by the ScreenPilot state machine."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BusinessException(ScreenPilotOutcome):
|
|
13
|
+
"""Raised from inside transitions to indicate a business process condition."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UnplannedTransitionsError(ScreenPilotException):
|
|
17
|
+
"""Raised when a transition occurs that is not planned in the state machine."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RepeatStatesError(ScreenPilotException):
|
|
21
|
+
"""Raised when an attempt is made to re-enter a state that should not be repeated."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RepeatTransitions(ScreenPilotException):
|
|
25
|
+
"""Raised when the same transition is attempted to be repeated in an invalid context."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CourseCorrectionImpossible(ScreenPilotException):
|
|
29
|
+
"""Raised when the state machine cannot determine a course correction."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SuccessfulCompletion(ScreenPilotOutcome):
|
|
33
|
+
"""Raised when the state machine has completed successfully."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RollbackCompleted(ScreenPilotOutcome):
|
|
37
|
+
"""Raised when the state machine has completed a rollback successfully."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def complete_ui_automation(message: Optional[str] = None):
|
|
41
|
+
"""Raise an exception to interrupt ScreenPilot and indicate that the UI automation has completed successfully."""
|
|
42
|
+
raise SuccessfulCompletion(message)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import BaseModel, field_validator, model_validator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ActionString(BaseModel):
|
|
6
|
+
"""
|
|
7
|
+
ActionString class represents a string that represents an action in an application.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
action_string (str): The string representation of the action.
|
|
11
|
+
comment (str, optional): An optional comment or description for the action.
|
|
12
|
+
interrupt_process (bool, optional): A flag indicating whether the action should interrupt the process.
|
|
13
|
+
|
|
14
|
+
Methods:
|
|
15
|
+
ensure_format(v: str) -> str:
|
|
16
|
+
Validator function that ensures the action string has the correct format.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
action_string: str
|
|
21
|
+
action_comment: Optional[str] = None
|
|
22
|
+
observation: Optional[str] = None
|
|
23
|
+
interrupt_process: Optional[bool] = False
|
|
24
|
+
|
|
25
|
+
@field_validator("action_string", mode="before")
|
|
26
|
+
@classmethod
|
|
27
|
+
def ensure_format(cls, v):
|
|
28
|
+
if not isinstance(v, str):
|
|
29
|
+
raise ValueError("Action string must be a string")
|
|
30
|
+
if not v.endswith(".do()") and not v.startswith("NoAction"):
|
|
31
|
+
raise ValueError("Action string must end with '.do()'")
|
|
32
|
+
return v
|
|
33
|
+
|
|
34
|
+
@field_validator("interrupt_process", mode="before")
|
|
35
|
+
def convert_to_bool(cls, v):
|
|
36
|
+
if v is None:
|
|
37
|
+
return False
|
|
38
|
+
elif isinstance(v, str):
|
|
39
|
+
return v.lower() in ["true", "1", "yes"]
|
|
40
|
+
return v
|