minitap-mobile-use 2.2.0__py3-none-any.whl → 2.4.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.
Potentially problematic release.
This version of minitap-mobile-use might be problematic. Click here for more details.
- minitap/mobile_use/agents/contextor/contextor.py +6 -4
- minitap/mobile_use/agents/cortex/cortex.md +114 -27
- minitap/mobile_use/agents/cortex/cortex.py +8 -5
- minitap/mobile_use/agents/executor/executor.md +15 -10
- minitap/mobile_use/agents/executor/executor.py +6 -5
- minitap/mobile_use/agents/executor/utils.py +2 -1
- minitap/mobile_use/agents/hopper/hopper.py +6 -3
- minitap/mobile_use/agents/orchestrator/orchestrator.py +26 -11
- minitap/mobile_use/agents/outputter/outputter.py +6 -3
- minitap/mobile_use/agents/outputter/test_outputter.py +104 -42
- minitap/mobile_use/agents/planner/planner.md +20 -22
- minitap/mobile_use/agents/planner/planner.py +10 -7
- minitap/mobile_use/agents/planner/types.py +4 -2
- minitap/mobile_use/agents/planner/utils.py +14 -0
- minitap/mobile_use/agents/summarizer/summarizer.py +2 -2
- minitap/mobile_use/config.py +6 -1
- minitap/mobile_use/context.py +13 -3
- minitap/mobile_use/controllers/mobile_command_controller.py +1 -14
- minitap/mobile_use/graph/state.py +7 -3
- minitap/mobile_use/sdk/agent.py +204 -29
- minitap/mobile_use/sdk/examples/README.md +19 -1
- minitap/mobile_use/sdk/examples/platform_minimal_example.py +46 -0
- minitap/mobile_use/sdk/services/platform.py +244 -0
- minitap/mobile_use/sdk/types/__init__.py +14 -14
- minitap/mobile_use/sdk/types/exceptions.py +57 -0
- minitap/mobile_use/sdk/types/platform.py +125 -0
- minitap/mobile_use/sdk/types/task.py +60 -17
- minitap/mobile_use/servers/device_hardware_bridge.py +3 -2
- minitap/mobile_use/servers/stop_servers.py +11 -12
- minitap/mobile_use/servers/utils.py +6 -9
- minitap/mobile_use/services/llm.py +89 -5
- minitap/mobile_use/tools/index.py +2 -8
- minitap/mobile_use/tools/mobile/back.py +3 -3
- minitap/mobile_use/tools/mobile/clear_text.py +67 -38
- minitap/mobile_use/tools/mobile/erase_one_char.py +5 -4
- minitap/mobile_use/tools/mobile/{take_screenshot.py → glimpse_screen.py} +23 -15
- minitap/mobile_use/tools/mobile/input_text.py +67 -16
- minitap/mobile_use/tools/mobile/launch_app.py +54 -22
- minitap/mobile_use/tools/mobile/long_press_on.py +15 -8
- minitap/mobile_use/tools/mobile/open_link.py +15 -8
- minitap/mobile_use/tools/mobile/press_key.py +15 -8
- minitap/mobile_use/tools/mobile/stop_app.py +14 -8
- minitap/mobile_use/tools/mobile/swipe.py +11 -5
- minitap/mobile_use/tools/mobile/tap.py +103 -21
- minitap/mobile_use/tools/mobile/wait_for_animation_to_end.py +3 -3
- minitap/mobile_use/tools/test_utils.py +377 -0
- minitap/mobile_use/tools/types.py +35 -0
- minitap/mobile_use/tools/utils.py +149 -39
- minitap/mobile_use/utils/recorder.py +1 -1
- minitap/mobile_use/utils/test_ui_hierarchy.py +178 -0
- minitap/mobile_use/utils/ui_hierarchy.py +11 -4
- {minitap_mobile_use-2.2.0.dist-info → minitap_mobile_use-2.4.0.dist-info}/METADATA +6 -4
- minitap_mobile_use-2.4.0.dist-info/RECORD +99 -0
- minitap/mobile_use/tools/mobile/copy_text_from.py +0 -73
- minitap/mobile_use/tools/mobile/find_packages.py +0 -69
- minitap/mobile_use/tools/mobile/paste_text.py +0 -62
- minitap_mobile_use-2.2.0.dist-info/RECORD +0 -96
- {minitap_mobile_use-2.2.0.dist-info → minitap_mobile_use-2.4.0.dist-info}/WHEEL +0 -0
- {minitap_mobile_use-2.2.0.dist-info → minitap_mobile_use-2.4.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Literal
|
|
3
|
+
from typing import Annotated, Literal
|
|
4
4
|
|
|
5
5
|
from langchain_core.messages import ToolMessage
|
|
6
6
|
from langchain_core.tools import tool
|
|
@@ -8,17 +8,19 @@ from langchain_core.tools.base import InjectedToolCallId
|
|
|
8
8
|
from langgraph.prebuilt import InjectedState
|
|
9
9
|
from langgraph.types import Command
|
|
10
10
|
from pydantic import BaseModel
|
|
11
|
-
from typing import Annotated
|
|
12
11
|
|
|
13
12
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
14
13
|
from minitap.mobile_use.context import MobileUseContext
|
|
14
|
+
from minitap.mobile_use.controllers.mobile_command_controller import get_screen_data
|
|
15
15
|
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
16
16
|
input_text as input_text_controller,
|
|
17
17
|
)
|
|
18
18
|
from minitap.mobile_use.graph.state import State
|
|
19
19
|
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
20
|
+
from minitap.mobile_use.tools.types import Target
|
|
20
21
|
from minitap.mobile_use.tools.utils import focus_element_if_needed, move_cursor_to_end_if_bounds
|
|
21
22
|
from minitap.mobile_use.utils.logger import get_logger
|
|
23
|
+
from minitap.mobile_use.utils.ui_hierarchy import find_element_by_resource_id, get_element_text
|
|
22
24
|
|
|
23
25
|
logger = get_logger(__name__)
|
|
24
26
|
|
|
@@ -42,12 +44,12 @@ def _controller_input_text(ctx: MobileUseContext, text: str) -> InputResult:
|
|
|
42
44
|
|
|
43
45
|
def get_input_text_tool(ctx: MobileUseContext):
|
|
44
46
|
@tool
|
|
45
|
-
def input_text(
|
|
47
|
+
async def input_text(
|
|
46
48
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
47
49
|
state: Annotated[State, InjectedState],
|
|
48
50
|
agent_thought: str,
|
|
49
51
|
text: str,
|
|
50
|
-
|
|
52
|
+
target: Target,
|
|
51
53
|
):
|
|
52
54
|
"""
|
|
53
55
|
Focus a text field and type text into it.
|
|
@@ -55,32 +57,69 @@ def get_input_text_tool(ctx: MobileUseContext):
|
|
|
55
57
|
- Ensure the corresponding element is focused (tap if necessary).
|
|
56
58
|
- If bounds are available, tap near the end to place the cursor at the end.
|
|
57
59
|
- Type the provided `text` using the controller.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
tool_call_id: The ID of the tool call.
|
|
63
|
+
state: The state of the agent.
|
|
64
|
+
agent_thought: The thought of the agent.
|
|
65
|
+
text: The text to type.
|
|
66
|
+
target: The target of the text input (if available).
|
|
58
67
|
"""
|
|
59
|
-
focused = focus_element_if_needed(ctx=ctx,
|
|
60
|
-
if focused:
|
|
61
|
-
|
|
68
|
+
focused = focus_element_if_needed(ctx=ctx, target=target)
|
|
69
|
+
if not focused:
|
|
70
|
+
error_message = "Failed to focus the text input element before typing."
|
|
71
|
+
tool_message = ToolMessage(
|
|
72
|
+
tool_call_id=tool_call_id,
|
|
73
|
+
content=input_text_wrapper.on_failure_fn(text, error_message),
|
|
74
|
+
additional_kwargs={"error": error_message},
|
|
75
|
+
status="error",
|
|
76
|
+
)
|
|
77
|
+
return Command(
|
|
78
|
+
update=await state.asanitize_update(
|
|
79
|
+
ctx=ctx,
|
|
80
|
+
update={
|
|
81
|
+
"agents_thoughts": [agent_thought, error_message],
|
|
82
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
83
|
+
},
|
|
84
|
+
agent="executor",
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
move_cursor_to_end_if_bounds(ctx=ctx, state=state, target=target)
|
|
62
89
|
|
|
63
90
|
result = _controller_input_text(ctx=ctx, text=text)
|
|
64
|
-
|
|
65
91
|
status: Literal["success", "error"] = "success" if result.ok else "error"
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
|
|
93
|
+
text_input_content = ""
|
|
94
|
+
if status == "success" and target.resource_id:
|
|
95
|
+
screen_data = get_screen_data(screen_api_client=ctx.screen_api_client)
|
|
96
|
+
state.latest_ui_hierarchy = screen_data.elements
|
|
97
|
+
element = find_element_by_resource_id(
|
|
98
|
+
ui_hierarchy=state.latest_ui_hierarchy,
|
|
99
|
+
resource_id=target.resource_id,
|
|
100
|
+
index=target.resource_id_index,
|
|
101
|
+
)
|
|
102
|
+
if element:
|
|
103
|
+
text_input_content = get_element_text(element)
|
|
104
|
+
|
|
105
|
+
agent_outcome = (
|
|
106
|
+
input_text_wrapper.on_success_fn(text, text_input_content, target.resource_id)
|
|
68
107
|
if result.ok
|
|
69
|
-
else input_text_wrapper.on_failure_fn(text)
|
|
108
|
+
else input_text_wrapper.on_failure_fn(text, result.error)
|
|
70
109
|
)
|
|
71
110
|
|
|
72
111
|
tool_message = ToolMessage(
|
|
73
112
|
tool_call_id=tool_call_id,
|
|
74
|
-
content=
|
|
113
|
+
content=agent_outcome,
|
|
75
114
|
additional_kwargs={"error": result.error} if not result.ok else {},
|
|
76
115
|
status=status,
|
|
77
116
|
)
|
|
78
117
|
|
|
79
118
|
return Command(
|
|
80
|
-
update=state.
|
|
119
|
+
update=await state.asanitize_update(
|
|
81
120
|
ctx=ctx,
|
|
82
121
|
update={
|
|
83
|
-
"agents_thoughts": [agent_thought],
|
|
122
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
84
123
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
85
124
|
},
|
|
86
125
|
agent="executor",
|
|
@@ -90,8 +129,20 @@ def get_input_text_tool(ctx: MobileUseContext):
|
|
|
90
129
|
return input_text
|
|
91
130
|
|
|
92
131
|
|
|
132
|
+
def _on_input_success(text, text_input_content, text_input_resource_id):
|
|
133
|
+
"""Success message handler for input text operations."""
|
|
134
|
+
if text_input_resource_id is not None:
|
|
135
|
+
return (
|
|
136
|
+
f"Typed {repr(text)}.\n"
|
|
137
|
+
f"Here is the whole content of input with id {repr(text_input_resource_id)}: "
|
|
138
|
+
f"{repr(text_input_content)}"
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
return "Typed text, should now verify before moving forward"
|
|
142
|
+
|
|
143
|
+
|
|
93
144
|
input_text_wrapper = ToolWrapper(
|
|
94
145
|
tool_fn_getter=get_input_text_tool,
|
|
95
|
-
on_success_fn=
|
|
96
|
-
on_failure_fn=lambda text: f"Failed to input text {text}",
|
|
146
|
+
on_success_fn=_on_input_success,
|
|
147
|
+
on_failure_fn=lambda text, error: f"Failed to input text {repr(text)}. Reason: {error}",
|
|
97
148
|
)
|
|
@@ -1,44 +1,76 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from langchain_core.messages import ToolMessage
|
|
2
4
|
from langchain_core.tools import tool
|
|
3
5
|
from langchain_core.tools.base import InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
4
7
|
from langgraph.types import Command
|
|
8
|
+
|
|
9
|
+
from minitap.mobile_use.agents.hopper.hopper import HopperOutput, hopper
|
|
5
10
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
11
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
6
12
|
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
7
13
|
launch_app as launch_app_controller,
|
|
8
14
|
)
|
|
9
|
-
from minitap.mobile_use.
|
|
10
|
-
from typing import Annotated
|
|
11
|
-
from minitap.mobile_use.context import MobileUseContext
|
|
15
|
+
from minitap.mobile_use.controllers.platform_specific_commands_controller import list_packages
|
|
12
16
|
from minitap.mobile_use.graph.state import State
|
|
13
|
-
from
|
|
17
|
+
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def find_package(ctx: MobileUseContext, app_name: str) -> str | None:
|
|
21
|
+
"""
|
|
22
|
+
Finds the package name for a given application name.
|
|
23
|
+
"""
|
|
24
|
+
all_packages = list_packages(ctx=ctx)
|
|
25
|
+
try:
|
|
26
|
+
hopper_output: HopperOutput = await hopper(
|
|
27
|
+
ctx=ctx,
|
|
28
|
+
request=f"I'm looking for the package name of the following app: '{app_name}'",
|
|
29
|
+
data=all_packages,
|
|
30
|
+
)
|
|
31
|
+
# Assuming hopper_output.output directly contains the package name
|
|
32
|
+
return hopper_output.output
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print(f"Failed to find package for '{app_name}': {e}")
|
|
35
|
+
return None
|
|
14
36
|
|
|
15
37
|
|
|
16
38
|
def get_launch_app_tool(ctx: MobileUseContext):
|
|
17
39
|
@tool
|
|
18
|
-
def launch_app(
|
|
40
|
+
async def launch_app(
|
|
19
41
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
20
42
|
state: Annotated[State, InjectedState],
|
|
43
|
+
app_name: str,
|
|
21
44
|
agent_thought: str,
|
|
22
|
-
|
|
23
|
-
):
|
|
45
|
+
) -> Command:
|
|
24
46
|
"""
|
|
25
|
-
|
|
47
|
+
Finds and launches an application on the device using its natural language name.
|
|
26
48
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
package_name = await find_package(ctx=ctx, app_name=app_name)
|
|
50
|
+
|
|
51
|
+
if not package_name:
|
|
52
|
+
tool_message = ToolMessage(
|
|
53
|
+
tool_call_id=tool_call_id,
|
|
54
|
+
content=launch_app_wrapper.on_failure_fn(app_name, "Package not found."),
|
|
55
|
+
status="error",
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
output = launch_app_controller(ctx=ctx, package_name=package_name)
|
|
59
|
+
has_failed = output is not None
|
|
60
|
+
tool_message = ToolMessage(
|
|
61
|
+
tool_call_id=tool_call_id,
|
|
62
|
+
content=launch_app_wrapper.on_failure_fn(app_name, output)
|
|
63
|
+
if has_failed
|
|
64
|
+
else launch_app_wrapper.on_success_fn(app_name),
|
|
65
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
66
|
+
status="error" if has_failed else "success",
|
|
67
|
+
)
|
|
68
|
+
|
|
37
69
|
return Command(
|
|
38
|
-
update=state.
|
|
70
|
+
update=await state.asanitize_update(
|
|
39
71
|
ctx=ctx,
|
|
40
72
|
update={
|
|
41
|
-
"agents_thoughts": [agent_thought],
|
|
73
|
+
"agents_thoughts": [agent_thought, tool_message.content],
|
|
42
74
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
43
75
|
},
|
|
44
76
|
agent="executor",
|
|
@@ -50,6 +82,6 @@ def get_launch_app_tool(ctx: MobileUseContext):
|
|
|
50
82
|
|
|
51
83
|
launch_app_wrapper = ToolWrapper(
|
|
52
84
|
tool_fn_getter=get_launch_app_tool,
|
|
53
|
-
on_success_fn=lambda
|
|
54
|
-
on_failure_fn=lambda
|
|
85
|
+
on_success_fn=lambda app_name: f"App '{app_name}' launched successfully.",
|
|
86
|
+
on_failure_fn=lambda app_name, error: f"Failed to launch app '{app_name}': {error}",
|
|
55
87
|
)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from langchain_core.messages import ToolMessage
|
|
2
4
|
from langchain_core.tools import tool
|
|
3
5
|
from langchain_core.tools.base import InjectedToolCallId
|
|
4
6
|
from langgraph.prebuilt import InjectedState
|
|
5
7
|
from langgraph.types import Command
|
|
8
|
+
|
|
6
9
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
7
10
|
from minitap.mobile_use.context import MobileUseContext
|
|
8
11
|
from minitap.mobile_use.controllers.mobile_command_controller import SelectorRequest
|
|
@@ -11,37 +14,41 @@ from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
|
11
14
|
)
|
|
12
15
|
from minitap.mobile_use.graph.state import State
|
|
13
16
|
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
14
|
-
from typing import Annotated
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def get_long_press_on_tool(ctx: MobileUseContext):
|
|
18
20
|
@tool
|
|
19
|
-
def long_press_on(
|
|
21
|
+
async def long_press_on(
|
|
20
22
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
23
|
state: Annotated[State, InjectedState],
|
|
22
24
|
agent_thought: str,
|
|
23
25
|
selector_request: SelectorRequest,
|
|
24
26
|
index: int | None = None,
|
|
25
|
-
):
|
|
27
|
+
) -> Command:
|
|
26
28
|
"""
|
|
27
29
|
Long press on a UI element identified by the given selector.
|
|
28
30
|
An index can be specified to select a specific element if multiple are found.
|
|
29
31
|
"""
|
|
30
32
|
output = long_press_on_controller(ctx=ctx, selector_request=selector_request, index=index)
|
|
31
33
|
has_failed = output is not None
|
|
34
|
+
|
|
35
|
+
agent_outcome = (
|
|
36
|
+
long_press_on_wrapper.on_failure_fn()
|
|
37
|
+
if has_failed
|
|
38
|
+
else long_press_on_wrapper.on_success_fn()
|
|
39
|
+
)
|
|
40
|
+
|
|
32
41
|
tool_message = ToolMessage(
|
|
33
42
|
tool_call_id=tool_call_id,
|
|
34
|
-
content=
|
|
35
|
-
if has_failed
|
|
36
|
-
else long_press_on_wrapper.on_success_fn(),
|
|
43
|
+
content=agent_outcome,
|
|
37
44
|
additional_kwargs={"error": output} if has_failed else {},
|
|
38
45
|
status="error" if has_failed else "success",
|
|
39
46
|
)
|
|
40
47
|
return Command(
|
|
41
|
-
update=state.
|
|
48
|
+
update=await state.asanitize_update(
|
|
42
49
|
ctx=ctx,
|
|
43
50
|
update={
|
|
44
|
-
"agents_thoughts": [agent_thought],
|
|
51
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
45
52
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
46
53
|
},
|
|
47
54
|
agent="executor",
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from langchain_core.messages import ToolMessage
|
|
2
4
|
from langchain_core.tools import tool
|
|
3
5
|
from langchain_core.tools.base import InjectedToolCallId
|
|
4
6
|
from langgraph.prebuilt import InjectedState
|
|
5
7
|
from langgraph.types import Command
|
|
8
|
+
|
|
6
9
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
7
10
|
from minitap.mobile_use.context import MobileUseContext
|
|
8
11
|
from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
@@ -10,35 +13,39 @@ from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
|
10
13
|
)
|
|
11
14
|
from minitap.mobile_use.graph.state import State
|
|
12
15
|
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
13
|
-
from typing import Annotated
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def get_open_link_tool(ctx: MobileUseContext):
|
|
17
19
|
@tool
|
|
18
|
-
def open_link(
|
|
20
|
+
async def open_link(
|
|
19
21
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
20
22
|
state: Annotated[State, InjectedState],
|
|
21
23
|
agent_thought: str,
|
|
22
24
|
url: str,
|
|
23
|
-
):
|
|
25
|
+
) -> Command:
|
|
24
26
|
"""
|
|
25
27
|
Open a link on a device (i.e. a deep link).
|
|
26
28
|
"""
|
|
27
29
|
output = open_link_controller(ctx=ctx, url=url)
|
|
28
30
|
has_failed = output is not None
|
|
31
|
+
|
|
32
|
+
agent_outcome = (
|
|
33
|
+
open_link_wrapper.on_failure_fn()
|
|
34
|
+
if has_failed
|
|
35
|
+
else open_link_wrapper.on_success_fn(url)
|
|
36
|
+
)
|
|
37
|
+
|
|
29
38
|
tool_message = ToolMessage(
|
|
30
39
|
tool_call_id=tool_call_id,
|
|
31
|
-
content=
|
|
32
|
-
if has_failed
|
|
33
|
-
else open_link_wrapper.on_success_fn(url),
|
|
40
|
+
content=agent_outcome,
|
|
34
41
|
additional_kwargs={"error": output} if has_failed else {},
|
|
35
42
|
status="error" if has_failed else "success",
|
|
36
43
|
)
|
|
37
44
|
return Command(
|
|
38
|
-
update=state.
|
|
45
|
+
update=await state.asanitize_update(
|
|
39
46
|
ctx=ctx,
|
|
40
47
|
update={
|
|
41
|
-
"agents_thoughts": [agent_thought],
|
|
48
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
42
49
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
43
50
|
},
|
|
44
51
|
agent="executor",
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from langchain_core.messages import ToolMessage
|
|
2
4
|
from langchain_core.tools import tool
|
|
3
5
|
from langchain_core.tools.base import InjectedToolCallId
|
|
4
6
|
from langgraph.prebuilt import InjectedState
|
|
5
7
|
from langgraph.types import Command
|
|
8
|
+
|
|
6
9
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
7
10
|
from minitap.mobile_use.context import MobileUseContext
|
|
8
11
|
from minitap.mobile_use.controllers.mobile_command_controller import Key
|
|
@@ -11,33 +14,37 @@ from minitap.mobile_use.controllers.mobile_command_controller import (
|
|
|
11
14
|
)
|
|
12
15
|
from minitap.mobile_use.graph.state import State
|
|
13
16
|
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
14
|
-
from typing import Annotated
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def get_press_key_tool(ctx: MobileUseContext):
|
|
18
20
|
@tool
|
|
19
|
-
def press_key(
|
|
21
|
+
async def press_key(
|
|
20
22
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
21
23
|
state: Annotated[State, InjectedState],
|
|
22
24
|
agent_thought: str,
|
|
23
25
|
key: Key,
|
|
24
|
-
):
|
|
26
|
+
) -> Command:
|
|
25
27
|
"""Press a key on the device."""
|
|
26
28
|
output = press_key_controller(ctx=ctx, key=key)
|
|
27
29
|
has_failed = output is not None
|
|
30
|
+
|
|
31
|
+
agent_outcome = (
|
|
32
|
+
press_key_wrapper.on_failure_fn(key)
|
|
33
|
+
if has_failed
|
|
34
|
+
else press_key_wrapper.on_success_fn(key)
|
|
35
|
+
)
|
|
36
|
+
|
|
28
37
|
tool_message = ToolMessage(
|
|
29
38
|
tool_call_id=tool_call_id,
|
|
30
|
-
content=
|
|
31
|
-
if has_failed
|
|
32
|
-
else press_key_wrapper.on_success_fn(key),
|
|
39
|
+
content=agent_outcome,
|
|
33
40
|
additional_kwargs={"error": output} if has_failed else {},
|
|
34
41
|
status="error" if has_failed else "success",
|
|
35
42
|
)
|
|
36
43
|
return Command(
|
|
37
|
-
update=state.
|
|
44
|
+
update=await state.asanitize_update(
|
|
38
45
|
ctx=ctx,
|
|
39
46
|
update={
|
|
40
|
-
"agents_thoughts": [agent_thought],
|
|
47
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
41
48
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
42
49
|
},
|
|
43
50
|
agent="executor",
|
|
@@ -1,43 +1,49 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
1
3
|
from langchain_core.messages import ToolMessage
|
|
2
4
|
from langchain_core.tools import tool
|
|
3
5
|
from langchain_core.tools.base import InjectedToolCallId
|
|
4
6
|
from langgraph.prebuilt import InjectedState
|
|
5
7
|
from langgraph.types import Command
|
|
8
|
+
|
|
6
9
|
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
7
10
|
from minitap.mobile_use.context import MobileUseContext
|
|
8
11
|
from minitap.mobile_use.controllers.mobile_command_controller import stop_app as stop_app_controller
|
|
9
12
|
from minitap.mobile_use.graph.state import State
|
|
10
13
|
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
11
|
-
from typing import Annotated
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def get_stop_app_tool(ctx: MobileUseContext):
|
|
15
17
|
@tool
|
|
16
|
-
def stop_app(
|
|
18
|
+
async def stop_app(
|
|
17
19
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
18
20
|
state: Annotated[State, InjectedState],
|
|
19
21
|
agent_thought: str,
|
|
20
22
|
package_name: str | None = None,
|
|
21
|
-
):
|
|
23
|
+
) -> Command:
|
|
22
24
|
"""
|
|
23
25
|
Stops current application if it is running.
|
|
24
26
|
You can also specify the package name of the app to be stopped.
|
|
25
27
|
"""
|
|
26
28
|
output = stop_app_controller(ctx=ctx, package_name=package_name)
|
|
27
29
|
has_failed = output is not None
|
|
30
|
+
|
|
31
|
+
agent_outcome = (
|
|
32
|
+
stop_app_wrapper.on_failure_fn(package_name)
|
|
33
|
+
if has_failed
|
|
34
|
+
else stop_app_wrapper.on_success_fn(package_name)
|
|
35
|
+
)
|
|
28
36
|
tool_message = ToolMessage(
|
|
29
37
|
tool_call_id=tool_call_id,
|
|
30
|
-
content=
|
|
31
|
-
if has_failed
|
|
32
|
-
else stop_app_wrapper.on_success_fn(package_name),
|
|
38
|
+
content=agent_outcome,
|
|
33
39
|
additional_kwargs={"error": output} if has_failed else {},
|
|
34
40
|
status="error" if has_failed else "success",
|
|
35
41
|
)
|
|
36
42
|
return Command(
|
|
37
|
-
update=state.
|
|
43
|
+
update=await state.asanitize_update(
|
|
38
44
|
ctx=ctx,
|
|
39
45
|
update={
|
|
40
|
-
"agents_thoughts": [agent_thought],
|
|
46
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
41
47
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
42
48
|
},
|
|
43
49
|
agent="executor",
|
|
@@ -24,26 +24,32 @@ from minitap.mobile_use.tools.tool_wrapper import CompositeToolWrapper
|
|
|
24
24
|
|
|
25
25
|
def get_swipe_tool(ctx: MobileUseContext) -> BaseTool:
|
|
26
26
|
@tool
|
|
27
|
-
def swipe(
|
|
27
|
+
async def swipe(
|
|
28
28
|
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
29
29
|
state: Annotated[State, InjectedState],
|
|
30
30
|
agent_thought: str,
|
|
31
31
|
swipe_request: SwipeRequest,
|
|
32
|
-
):
|
|
32
|
+
) -> Command:
|
|
33
33
|
"""Swipes on the screen."""
|
|
34
34
|
output = swipe_controller(ctx=ctx, swipe_request=swipe_request)
|
|
35
35
|
has_failed = output is not None
|
|
36
|
+
|
|
37
|
+
agent_outcome = (
|
|
38
|
+
swipe_wrapper.on_success_fn() if not has_failed else swipe_wrapper.on_failure_fn()
|
|
39
|
+
)
|
|
40
|
+
|
|
36
41
|
tool_message = ToolMessage(
|
|
37
42
|
tool_call_id=tool_call_id,
|
|
38
|
-
content=
|
|
43
|
+
content=agent_outcome,
|
|
39
44
|
additional_kwargs={"error": output} if has_failed else {},
|
|
40
45
|
status="error" if has_failed else "success",
|
|
41
46
|
)
|
|
47
|
+
|
|
42
48
|
return Command(
|
|
43
|
-
update=state.
|
|
49
|
+
update=await state.asanitize_update(
|
|
44
50
|
ctx=ctx,
|
|
45
51
|
update={
|
|
46
|
-
"agents_thoughts": [agent_thought],
|
|
52
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
47
53
|
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
48
54
|
},
|
|
49
55
|
agent="executor",
|