minitap-mobile-use 0.0.1.dev0__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.
Potentially problematic release.
This version of minitap-mobile-use might be problematic. Click here for more details.
- minitap/mobile_use/__init__.py +0 -0
- minitap/mobile_use/agents/contextor/contextor.py +42 -0
- minitap/mobile_use/agents/cortex/cortex.md +93 -0
- minitap/mobile_use/agents/cortex/cortex.py +107 -0
- minitap/mobile_use/agents/cortex/types.py +11 -0
- minitap/mobile_use/agents/executor/executor.md +73 -0
- minitap/mobile_use/agents/executor/executor.py +84 -0
- minitap/mobile_use/agents/executor/executor_context_cleaner.py +27 -0
- minitap/mobile_use/agents/executor/utils.py +11 -0
- minitap/mobile_use/agents/hopper/hopper.md +13 -0
- minitap/mobile_use/agents/hopper/hopper.py +45 -0
- minitap/mobile_use/agents/orchestrator/human.md +13 -0
- minitap/mobile_use/agents/orchestrator/orchestrator.md +18 -0
- minitap/mobile_use/agents/orchestrator/orchestrator.py +114 -0
- minitap/mobile_use/agents/orchestrator/types.py +14 -0
- minitap/mobile_use/agents/outputter/human.md +25 -0
- minitap/mobile_use/agents/outputter/outputter.py +75 -0
- minitap/mobile_use/agents/outputter/test_outputter.py +107 -0
- minitap/mobile_use/agents/planner/human.md +12 -0
- minitap/mobile_use/agents/planner/planner.md +64 -0
- minitap/mobile_use/agents/planner/planner.py +64 -0
- minitap/mobile_use/agents/planner/types.py +44 -0
- minitap/mobile_use/agents/planner/utils.py +45 -0
- minitap/mobile_use/agents/summarizer/summarizer.py +34 -0
- minitap/mobile_use/clients/device_hardware_client.py +23 -0
- minitap/mobile_use/clients/ios_client.py +44 -0
- minitap/mobile_use/clients/screen_api_client.py +53 -0
- minitap/mobile_use/config.py +285 -0
- minitap/mobile_use/constants.py +2 -0
- minitap/mobile_use/context.py +65 -0
- minitap/mobile_use/controllers/__init__.py +0 -0
- minitap/mobile_use/controllers/mobile_command_controller.py +379 -0
- minitap/mobile_use/controllers/platform_specific_commands_controller.py +74 -0
- minitap/mobile_use/graph/graph.py +149 -0
- minitap/mobile_use/graph/state.py +73 -0
- minitap/mobile_use/main.py +122 -0
- minitap/mobile_use/sdk/__init__.py +12 -0
- minitap/mobile_use/sdk/agent.py +524 -0
- minitap/mobile_use/sdk/builders/__init__.py +10 -0
- minitap/mobile_use/sdk/builders/agent_config_builder.py +213 -0
- minitap/mobile_use/sdk/builders/index.py +15 -0
- minitap/mobile_use/sdk/builders/task_request_builder.py +218 -0
- minitap/mobile_use/sdk/constants.py +14 -0
- minitap/mobile_use/sdk/examples/README.md +45 -0
- minitap/mobile_use/sdk/examples/__init__.py +1 -0
- minitap/mobile_use/sdk/examples/simple_photo_organizer.py +76 -0
- minitap/mobile_use/sdk/examples/smart_notification_assistant.py +177 -0
- minitap/mobile_use/sdk/types/__init__.py +49 -0
- minitap/mobile_use/sdk/types/agent.py +73 -0
- minitap/mobile_use/sdk/types/exceptions.py +74 -0
- minitap/mobile_use/sdk/types/task.py +191 -0
- minitap/mobile_use/sdk/utils.py +28 -0
- minitap/mobile_use/servers/config.py +19 -0
- minitap/mobile_use/servers/device_hardware_bridge.py +212 -0
- minitap/mobile_use/servers/device_screen_api.py +143 -0
- minitap/mobile_use/servers/start_servers.py +151 -0
- minitap/mobile_use/servers/stop_servers.py +215 -0
- minitap/mobile_use/servers/utils.py +11 -0
- minitap/mobile_use/services/accessibility.py +100 -0
- minitap/mobile_use/services/llm.py +143 -0
- minitap/mobile_use/tools/index.py +54 -0
- minitap/mobile_use/tools/mobile/back.py +52 -0
- minitap/mobile_use/tools/mobile/copy_text_from.py +77 -0
- minitap/mobile_use/tools/mobile/erase_text.py +124 -0
- minitap/mobile_use/tools/mobile/input_text.py +74 -0
- minitap/mobile_use/tools/mobile/launch_app.py +59 -0
- minitap/mobile_use/tools/mobile/list_packages.py +78 -0
- minitap/mobile_use/tools/mobile/long_press_on.py +62 -0
- minitap/mobile_use/tools/mobile/open_link.py +59 -0
- minitap/mobile_use/tools/mobile/paste_text.py +66 -0
- minitap/mobile_use/tools/mobile/press_key.py +58 -0
- minitap/mobile_use/tools/mobile/run_flow.py +57 -0
- minitap/mobile_use/tools/mobile/stop_app.py +58 -0
- minitap/mobile_use/tools/mobile/swipe.py +56 -0
- minitap/mobile_use/tools/mobile/take_screenshot.py +70 -0
- minitap/mobile_use/tools/mobile/tap.py +66 -0
- minitap/mobile_use/tools/mobile/wait_for_animation_to_end.py +68 -0
- minitap/mobile_use/tools/tool_wrapper.py +33 -0
- minitap/mobile_use/utils/cli_helpers.py +40 -0
- minitap/mobile_use/utils/cli_selection.py +144 -0
- minitap/mobile_use/utils/conversations.py +31 -0
- minitap/mobile_use/utils/decorators.py +123 -0
- minitap/mobile_use/utils/errors.py +6 -0
- minitap/mobile_use/utils/file.py +13 -0
- minitap/mobile_use/utils/logger.py +184 -0
- minitap/mobile_use/utils/media.py +73 -0
- minitap/mobile_use/utils/recorder.py +55 -0
- minitap/mobile_use/utils/requests_utils.py +37 -0
- minitap/mobile_use/utils/shell_utils.py +20 -0
- minitap/mobile_use/utils/time.py +6 -0
- minitap/mobile_use/utils/ui_hierarchy.py +30 -0
- minitap_mobile_use-0.0.1.dev0.dist-info/METADATA +274 -0
- minitap_mobile_use-0.0.1.dev0.dist-info/RECORD +95 -0
- minitap_mobile_use-0.0.1.dev0.dist-info/WHEEL +4 -0
- minitap_mobile_use-0.0.1.dev0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
from typing_extensions import Annotated
|
|
9
|
+
|
|
10
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
11
|
+
ScreenDataResponse,
|
|
12
|
+
WaitTimeout,
|
|
13
|
+
get_screen_data,
|
|
14
|
+
wait_for_animation_to_end,
|
|
15
|
+
)
|
|
16
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
17
|
+
erase_text as erase_text_controller,
|
|
18
|
+
)
|
|
19
|
+
from minitap.mobile_use.graph.state import State
|
|
20
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
21
|
+
from minitap.mobile_use.utils.ui_hierarchy import find_element_by_resource_id
|
|
22
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_erase_text_tool(ctx: MobileUseContext):
|
|
26
|
+
@tool
|
|
27
|
+
def erase_text(
|
|
28
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
29
|
+
state: Annotated[State, InjectedState],
|
|
30
|
+
agent_thought: str,
|
|
31
|
+
input_text_resource_id: str,
|
|
32
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
33
|
+
nb_chars: Optional[int] = None,
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Erases up to `nb_chars` characters from the currently selected text field (default: 50).
|
|
37
|
+
|
|
38
|
+
iOS Note:
|
|
39
|
+
This may be flaky on iOS. As a workaround:
|
|
40
|
+
- long_press_on("<input id>")
|
|
41
|
+
- tap_on("Select All")
|
|
42
|
+
- erase_text()
|
|
43
|
+
|
|
44
|
+
Matches 'clearText' in search.
|
|
45
|
+
"""
|
|
46
|
+
# value of text key from input_text_ressource_id
|
|
47
|
+
latest_ui_hierarchy = state.latest_ui_hierarchy
|
|
48
|
+
previous_text_value = None
|
|
49
|
+
new_text_value = None
|
|
50
|
+
nb_char_erased = -1
|
|
51
|
+
if latest_ui_hierarchy is not None:
|
|
52
|
+
text_input_element = find_element_by_resource_id(
|
|
53
|
+
ui_hierarchy=latest_ui_hierarchy, resource_id=input_text_resource_id
|
|
54
|
+
)
|
|
55
|
+
if text_input_element:
|
|
56
|
+
previous_text_value = text_input_element.get("text", None)
|
|
57
|
+
|
|
58
|
+
output = erase_text_controller(ctx=ctx, nb_chars=nb_chars)
|
|
59
|
+
has_failed = output is not None
|
|
60
|
+
|
|
61
|
+
wait_for_animation_to_end(ctx=ctx, timeout=WaitTimeout.MEDIUM)
|
|
62
|
+
|
|
63
|
+
screen_data: ScreenDataResponse = get_screen_data(screen_api_client=ctx.screen_api_client)
|
|
64
|
+
latest_ui_hierarchy = screen_data.elements
|
|
65
|
+
|
|
66
|
+
if not has_failed and latest_ui_hierarchy is not None:
|
|
67
|
+
text_input_element = find_element_by_resource_id(
|
|
68
|
+
ui_hierarchy=latest_ui_hierarchy, resource_id=input_text_resource_id
|
|
69
|
+
)
|
|
70
|
+
if text_input_element:
|
|
71
|
+
new_text_value = text_input_element.get("text", None)
|
|
72
|
+
|
|
73
|
+
if previous_text_value is not None and new_text_value is not None:
|
|
74
|
+
if previous_text_value == new_text_value:
|
|
75
|
+
has_failed = True
|
|
76
|
+
output = (
|
|
77
|
+
"Unable to erase text: text is very likely a placeholder."
|
|
78
|
+
" Thus, assuming the text input is empty."
|
|
79
|
+
)
|
|
80
|
+
else:
|
|
81
|
+
nb_char_erased = len(previous_text_value) - len(new_text_value)
|
|
82
|
+
tool_message = ToolMessage(
|
|
83
|
+
tool_call_id=tool_call_id,
|
|
84
|
+
content=erase_text_wrapper.on_failure_fn(output)
|
|
85
|
+
if has_failed
|
|
86
|
+
else erase_text_wrapper.on_success_fn(
|
|
87
|
+
nb_char_erased=nb_char_erased, new_text_value=new_text_value
|
|
88
|
+
),
|
|
89
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return Command(
|
|
93
|
+
update=erase_text_wrapper.handle_executor_state_fields(
|
|
94
|
+
ctx=ctx,
|
|
95
|
+
state=state,
|
|
96
|
+
executor_metadata=executor_metadata,
|
|
97
|
+
tool_message=tool_message,
|
|
98
|
+
is_failure=has_failed,
|
|
99
|
+
updates={
|
|
100
|
+
"agents_thoughts": [agent_thought],
|
|
101
|
+
"messages": [tool_message],
|
|
102
|
+
},
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return erase_text
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def format_success_message(nb_char_erased: int, new_text_value: Optional[str]):
|
|
110
|
+
output = ""
|
|
111
|
+
if nb_char_erased == -1:
|
|
112
|
+
output = "Text erased successfully."
|
|
113
|
+
else:
|
|
114
|
+
output = f"Text erased successfully. {nb_char_erased} characters were erased."
|
|
115
|
+
if new_text_value is not None:
|
|
116
|
+
output += f" New text in the input is {new_text_value}."
|
|
117
|
+
return output
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
erase_text_wrapper = ToolWrapper(
|
|
121
|
+
tool_fn_getter=get_erase_text_tool,
|
|
122
|
+
on_success_fn=format_success_message,
|
|
123
|
+
on_failure_fn=lambda output: "Failed to erase text. " + output,
|
|
124
|
+
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.types import Command
|
|
7
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
8
|
+
input_text as input_text_controller,
|
|
9
|
+
)
|
|
10
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
11
|
+
from typing_extensions import Annotated
|
|
12
|
+
from minitap.mobile_use.graph.state import State
|
|
13
|
+
from langgraph.prebuilt import InjectedState
|
|
14
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_input_text_tool(ctx: MobileUseContext):
|
|
18
|
+
@tool
|
|
19
|
+
def input_text(
|
|
20
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
|
+
state: Annotated[State, InjectedState],
|
|
22
|
+
agent_thought: str,
|
|
23
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
24
|
+
text: str,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Inputs the specified text into the UI (works even if no field is focused).
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
- inputText: "Hello World"
|
|
31
|
+
|
|
32
|
+
Notes:
|
|
33
|
+
- Unicode not supported on Android.
|
|
34
|
+
|
|
35
|
+
Random Input Options:
|
|
36
|
+
- inputRandomEmail
|
|
37
|
+
- inputRandomPersonName
|
|
38
|
+
- inputRandomNumber (with optional 'length', default 8)
|
|
39
|
+
- inputRandomText (with optional 'length', default 8)
|
|
40
|
+
|
|
41
|
+
Tip:
|
|
42
|
+
Use `copyTextFrom` to reuse generated inputs in later steps.
|
|
43
|
+
"""
|
|
44
|
+
output = input_text_controller(ctx=ctx, text=text)
|
|
45
|
+
has_failed = output is not None
|
|
46
|
+
tool_message = ToolMessage(
|
|
47
|
+
tool_call_id=tool_call_id,
|
|
48
|
+
content=input_text_wrapper.on_failure_fn(text)
|
|
49
|
+
if has_failed
|
|
50
|
+
else input_text_wrapper.on_success_fn(text),
|
|
51
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
52
|
+
)
|
|
53
|
+
return Command(
|
|
54
|
+
update=input_text_wrapper.handle_executor_state_fields(
|
|
55
|
+
ctx=ctx,
|
|
56
|
+
state=state,
|
|
57
|
+
executor_metadata=executor_metadata,
|
|
58
|
+
tool_message=tool_message,
|
|
59
|
+
is_failure=has_failed,
|
|
60
|
+
updates={
|
|
61
|
+
"agents_thoughts": [agent_thought],
|
|
62
|
+
"messages": [tool_message],
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return input_text
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
input_text_wrapper = ToolWrapper(
|
|
71
|
+
tool_fn_getter=get_input_text_tool,
|
|
72
|
+
on_success_fn=lambda text: f"Successfully typed {text}",
|
|
73
|
+
on_failure_fn=lambda text: f"Failed to input text {text}",
|
|
74
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.types import Command
|
|
7
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
8
|
+
launch_app as launch_app_controller,
|
|
9
|
+
)
|
|
10
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
11
|
+
from typing_extensions import Annotated
|
|
12
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
13
|
+
from minitap.mobile_use.graph.state import State
|
|
14
|
+
from langgraph.prebuilt import InjectedState
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_launch_app_tool(ctx: MobileUseContext):
|
|
18
|
+
@tool
|
|
19
|
+
def launch_app(
|
|
20
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
|
+
state: Annotated[State, InjectedState],
|
|
22
|
+
agent_thought: str,
|
|
23
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
24
|
+
package_name: str,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Launch an application on the device using the package name on Android, bundle id on iOS.
|
|
28
|
+
"""
|
|
29
|
+
output = launch_app_controller(ctx=ctx, package_name=package_name)
|
|
30
|
+
has_failed = output is not None
|
|
31
|
+
tool_message = ToolMessage(
|
|
32
|
+
tool_call_id=tool_call_id,
|
|
33
|
+
content=launch_app_wrapper.on_failure_fn(package_name)
|
|
34
|
+
if has_failed
|
|
35
|
+
else launch_app_wrapper.on_success_fn(package_name),
|
|
36
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
37
|
+
)
|
|
38
|
+
return Command(
|
|
39
|
+
update=launch_app_wrapper.handle_executor_state_fields(
|
|
40
|
+
ctx=ctx,
|
|
41
|
+
state=state,
|
|
42
|
+
executor_metadata=executor_metadata,
|
|
43
|
+
tool_message=tool_message,
|
|
44
|
+
is_failure=has_failed,
|
|
45
|
+
updates={
|
|
46
|
+
"agents_thoughts": [agent_thought],
|
|
47
|
+
"messages": [tool_message],
|
|
48
|
+
},
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return launch_app
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
launch_app_wrapper = ToolWrapper(
|
|
56
|
+
tool_fn_getter=get_launch_app_tool,
|
|
57
|
+
on_success_fn=lambda package_name: f"App {package_name} launched successfully.",
|
|
58
|
+
on_failure_fn=lambda package_name: f"Failed to launch app {package_name}.",
|
|
59
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
from minitap.mobile_use.agents.hopper.hopper import HopperOutput, hopper
|
|
9
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
10
|
+
from minitap.mobile_use.controllers.platform_specific_commands_controller import (
|
|
11
|
+
list_packages as list_packages_command,
|
|
12
|
+
)
|
|
13
|
+
from minitap.mobile_use.graph.state import State
|
|
14
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
15
|
+
from typing_extensions import Annotated
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_list_packages_tool(ctx: MobileUseContext):
|
|
19
|
+
@tool
|
|
20
|
+
async def list_packages(
|
|
21
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
22
|
+
state: Annotated[State, InjectedState],
|
|
23
|
+
agent_thought: str,
|
|
24
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
25
|
+
) -> Command:
|
|
26
|
+
"""
|
|
27
|
+
Lists all the applications on the device.
|
|
28
|
+
Outputs the full package names list (android) or bundle ids list (IOS).
|
|
29
|
+
"""
|
|
30
|
+
output: str = list_packages_command(ctx=ctx)
|
|
31
|
+
has_failed = False
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
hopper_output: HopperOutput = await hopper(
|
|
35
|
+
ctx=ctx,
|
|
36
|
+
initial_goal=state.initial_goal,
|
|
37
|
+
messages=state.messages,
|
|
38
|
+
data=output,
|
|
39
|
+
)
|
|
40
|
+
tool_message = ToolMessage(
|
|
41
|
+
tool_call_id=tool_call_id,
|
|
42
|
+
content=list_packages_wrapper.on_success_fn()
|
|
43
|
+
+ ": "
|
|
44
|
+
+ hopper_output.step
|
|
45
|
+
+ ": "
|
|
46
|
+
+ hopper_output.output,
|
|
47
|
+
)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print("Failed to extract insights from data: " + str(e))
|
|
50
|
+
tool_message = ToolMessage(
|
|
51
|
+
tool_call_id=tool_call_id,
|
|
52
|
+
content=list_packages_wrapper.on_failure_fn(),
|
|
53
|
+
additional_kwargs={"output": output},
|
|
54
|
+
)
|
|
55
|
+
has_failed = True
|
|
56
|
+
|
|
57
|
+
return Command(
|
|
58
|
+
update=list_packages_wrapper.handle_executor_state_fields(
|
|
59
|
+
ctx=ctx,
|
|
60
|
+
state=state,
|
|
61
|
+
executor_metadata=executor_metadata,
|
|
62
|
+
tool_message=tool_message,
|
|
63
|
+
is_failure=has_failed,
|
|
64
|
+
updates={
|
|
65
|
+
"agents_thoughts": [agent_thought],
|
|
66
|
+
"messages": [tool_message],
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return list_packages
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
list_packages_wrapper = ToolWrapper(
|
|
75
|
+
tool_fn_getter=get_list_packages_tool,
|
|
76
|
+
on_success_fn=lambda: "Packages listed successfully.",
|
|
77
|
+
on_failure_fn=lambda: "Failed to list packages.",
|
|
78
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
9
|
+
from minitap.mobile_use.controllers.mobile_command_controller import SelectorRequest
|
|
10
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
11
|
+
long_press_on as long_press_on_controller,
|
|
12
|
+
)
|
|
13
|
+
from minitap.mobile_use.graph.state import State
|
|
14
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
15
|
+
from typing_extensions import Annotated
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_long_press_on_tool(ctx: MobileUseContext):
|
|
19
|
+
@tool
|
|
20
|
+
def long_press_on(
|
|
21
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
22
|
+
state: Annotated[State, InjectedState],
|
|
23
|
+
agent_thought: str,
|
|
24
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
25
|
+
selector_request: SelectorRequest,
|
|
26
|
+
index: Optional[int] = None,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Long press on a UI element identified by the given selector.
|
|
30
|
+
An index can be specified to select a specific element if multiple are found.
|
|
31
|
+
"""
|
|
32
|
+
output = long_press_on_controller(ctx=ctx, selector_request=selector_request, index=index)
|
|
33
|
+
has_failed = output is not None
|
|
34
|
+
tool_message = ToolMessage(
|
|
35
|
+
tool_call_id=tool_call_id,
|
|
36
|
+
content=long_press_on_wrapper.on_failure_fn()
|
|
37
|
+
if has_failed
|
|
38
|
+
else long_press_on_wrapper.on_success_fn(),
|
|
39
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
40
|
+
)
|
|
41
|
+
return Command(
|
|
42
|
+
update=long_press_on_wrapper.handle_executor_state_fields(
|
|
43
|
+
ctx=ctx,
|
|
44
|
+
state=state,
|
|
45
|
+
executor_metadata=executor_metadata,
|
|
46
|
+
tool_message=tool_message,
|
|
47
|
+
is_failure=has_failed,
|
|
48
|
+
updates={
|
|
49
|
+
"agents_thoughts": [agent_thought],
|
|
50
|
+
"messages": [tool_message],
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return long_press_on
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
long_press_on_wrapper = ToolWrapper(
|
|
59
|
+
tool_fn_getter=get_long_press_on_tool,
|
|
60
|
+
on_success_fn=lambda: "Long press on is successful.",
|
|
61
|
+
on_failure_fn=lambda: "Failed to long press on.",
|
|
62
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
9
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
10
|
+
open_link as open_link_controller,
|
|
11
|
+
)
|
|
12
|
+
from minitap.mobile_use.graph.state import State
|
|
13
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
14
|
+
from typing_extensions import Annotated
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_open_link_tool(ctx: MobileUseContext):
|
|
18
|
+
@tool
|
|
19
|
+
def open_link(
|
|
20
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
|
+
state: Annotated[State, InjectedState],
|
|
22
|
+
agent_thought: str,
|
|
23
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
24
|
+
url: str,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Open a link on a device (i.e. a deep link).
|
|
28
|
+
"""
|
|
29
|
+
output = open_link_controller(ctx=ctx, url=url)
|
|
30
|
+
has_failed = output is not None
|
|
31
|
+
tool_message = ToolMessage(
|
|
32
|
+
tool_call_id=tool_call_id,
|
|
33
|
+
content=open_link_wrapper.on_failure_fn()
|
|
34
|
+
if has_failed
|
|
35
|
+
else open_link_wrapper.on_success_fn(url),
|
|
36
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
37
|
+
)
|
|
38
|
+
return Command(
|
|
39
|
+
update=open_link_wrapper.handle_executor_state_fields(
|
|
40
|
+
ctx=ctx,
|
|
41
|
+
state=state,
|
|
42
|
+
executor_metadata=executor_metadata,
|
|
43
|
+
tool_message=tool_message,
|
|
44
|
+
is_failure=has_failed,
|
|
45
|
+
updates={
|
|
46
|
+
"agents_thoughts": [agent_thought],
|
|
47
|
+
"messages": [tool_message],
|
|
48
|
+
},
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return open_link
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
open_link_wrapper = ToolWrapper(
|
|
56
|
+
tool_fn_getter=get_open_link_tool,
|
|
57
|
+
on_success_fn=lambda url: f"Link {url} opened successfully.",
|
|
58
|
+
on_failure_fn=lambda: "Failed to open link.",
|
|
59
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.types import Command
|
|
7
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
8
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
9
|
+
paste_text as paste_text_controller,
|
|
10
|
+
)
|
|
11
|
+
from minitap.mobile_use.graph.state import State
|
|
12
|
+
from langgraph.prebuilt import InjectedState
|
|
13
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
14
|
+
from typing_extensions import Annotated
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_paste_text_tool(ctx: MobileUseContext):
|
|
18
|
+
@tool
|
|
19
|
+
def paste_text(
|
|
20
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
|
+
state: Annotated[State, InjectedState],
|
|
22
|
+
agent_thought: str,
|
|
23
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Pastes text previously copied via `copyTextFrom` into the currently focused field.
|
|
27
|
+
|
|
28
|
+
Note:
|
|
29
|
+
The text field must be focused before using this command.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
- copyTextFrom: { id: "someId" }
|
|
33
|
+
- tapOn: { id: "searchFieldId" }
|
|
34
|
+
- pasteText
|
|
35
|
+
"""
|
|
36
|
+
output = paste_text_controller(ctx=ctx)
|
|
37
|
+
has_failed = output is not None
|
|
38
|
+
tool_message = ToolMessage(
|
|
39
|
+
tool_call_id=tool_call_id,
|
|
40
|
+
content=paste_text_wrapper.on_failure_fn()
|
|
41
|
+
if has_failed
|
|
42
|
+
else paste_text_wrapper.on_success_fn(),
|
|
43
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
44
|
+
)
|
|
45
|
+
return Command(
|
|
46
|
+
update=paste_text_wrapper.handle_executor_state_fields(
|
|
47
|
+
ctx=ctx,
|
|
48
|
+
state=state,
|
|
49
|
+
executor_metadata=executor_metadata,
|
|
50
|
+
tool_message=tool_message,
|
|
51
|
+
is_failure=has_failed,
|
|
52
|
+
updates={
|
|
53
|
+
"agents_thoughts": [agent_thought],
|
|
54
|
+
"messages": [tool_message],
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return paste_text
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
paste_text_wrapper = ToolWrapper(
|
|
63
|
+
tool_fn_getter=get_paste_text_tool,
|
|
64
|
+
on_success_fn=lambda: "Text pasted successfully.",
|
|
65
|
+
on_failure_fn=lambda: "Failed to paste text.",
|
|
66
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
9
|
+
from minitap.mobile_use.controllers.mobile_command_controller import Key
|
|
10
|
+
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
11
|
+
press_key as press_key_controller,
|
|
12
|
+
)
|
|
13
|
+
from minitap.mobile_use.graph.state import State
|
|
14
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
15
|
+
from typing_extensions import Annotated
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_press_key_tool(ctx: MobileUseContext):
|
|
19
|
+
@tool
|
|
20
|
+
def press_key(
|
|
21
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
22
|
+
state: Annotated[State, InjectedState],
|
|
23
|
+
agent_thought: str,
|
|
24
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
25
|
+
key: Key,
|
|
26
|
+
):
|
|
27
|
+
"""Press a key on the device."""
|
|
28
|
+
output = press_key_controller(ctx=ctx, key=key)
|
|
29
|
+
has_failed = output is not None
|
|
30
|
+
tool_message = ToolMessage(
|
|
31
|
+
tool_call_id=tool_call_id,
|
|
32
|
+
content=press_key_wrapper.on_failure_fn(key)
|
|
33
|
+
if has_failed
|
|
34
|
+
else press_key_wrapper.on_success_fn(key),
|
|
35
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
36
|
+
)
|
|
37
|
+
return Command(
|
|
38
|
+
update=press_key_wrapper.handle_executor_state_fields(
|
|
39
|
+
ctx=ctx,
|
|
40
|
+
state=state,
|
|
41
|
+
executor_metadata=executor_metadata,
|
|
42
|
+
tool_message=tool_message,
|
|
43
|
+
is_failure=has_failed,
|
|
44
|
+
updates={
|
|
45
|
+
"agents_thoughts": [agent_thought],
|
|
46
|
+
"messages": [tool_message],
|
|
47
|
+
},
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return press_key
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
press_key_wrapper = ToolWrapper(
|
|
55
|
+
tool_fn_getter=get_press_key_tool,
|
|
56
|
+
on_success_fn=lambda key: f"Key {key.value} pressed successfully.",
|
|
57
|
+
on_failure_fn=lambda key: f"Failed to press key {key.value}.",
|
|
58
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from langchain_core.messages import ToolMessage
|
|
3
|
+
from langchain_core.tools import tool
|
|
4
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
5
|
+
from langgraph.prebuilt import InjectedState
|
|
6
|
+
from langgraph.types import Command
|
|
7
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
8
|
+
from minitap.mobile_use.controllers.mobile_command_controller import run_flow as run_flow_controller
|
|
9
|
+
from minitap.mobile_use.graph.state import State
|
|
10
|
+
from minitap.mobile_use.tools.tool_wrapper import ExecutorMetadata, ToolWrapper
|
|
11
|
+
from typing_extensions import Annotated
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_run_flow_tool(ctx: MobileUseContext):
|
|
15
|
+
@tool
|
|
16
|
+
def run_flow(
|
|
17
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
18
|
+
state: Annotated[State, InjectedState],
|
|
19
|
+
agent_thought: str,
|
|
20
|
+
flow_steps: list,
|
|
21
|
+
executor_metadata: Optional[ExecutorMetadata],
|
|
22
|
+
dry_run: bool = False,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
Run a flow i.e, a sequence of commands.
|
|
26
|
+
"""
|
|
27
|
+
output = run_flow_controller(ctx=ctx, flow_steps=flow_steps, dry_run=dry_run)
|
|
28
|
+
has_failed = output is not None
|
|
29
|
+
tool_message = ToolMessage(
|
|
30
|
+
tool_call_id=tool_call_id,
|
|
31
|
+
content=run_flow_wrapper.on_failure_fn()
|
|
32
|
+
if has_failed
|
|
33
|
+
else run_flow_wrapper.on_success_fn(),
|
|
34
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
35
|
+
)
|
|
36
|
+
return Command(
|
|
37
|
+
update=run_flow_wrapper.handle_executor_state_fields(
|
|
38
|
+
ctx=ctx,
|
|
39
|
+
state=state,
|
|
40
|
+
executor_metadata=executor_metadata,
|
|
41
|
+
tool_message=tool_message,
|
|
42
|
+
is_failure=has_failed,
|
|
43
|
+
updates={
|
|
44
|
+
"agents_thoughts": [agent_thought],
|
|
45
|
+
"messages": [tool_message],
|
|
46
|
+
},
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return run_flow
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
run_flow_wrapper = ToolWrapper(
|
|
54
|
+
tool_fn_getter=get_run_flow_tool,
|
|
55
|
+
on_success_fn=lambda: "Flow run successfully.",
|
|
56
|
+
on_failure_fn=lambda: "Failed to run flow.",
|
|
57
|
+
)
|