minitap-mobile-use 3.3.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.
- minitap/mobile_use/__init__.py +0 -0
- minitap/mobile_use/agents/contextor/contextor.md +55 -0
- minitap/mobile_use/agents/contextor/contextor.py +175 -0
- minitap/mobile_use/agents/contextor/types.py +36 -0
- minitap/mobile_use/agents/cortex/cortex.md +135 -0
- minitap/mobile_use/agents/cortex/cortex.py +152 -0
- minitap/mobile_use/agents/cortex/types.py +15 -0
- minitap/mobile_use/agents/executor/executor.md +42 -0
- minitap/mobile_use/agents/executor/executor.py +87 -0
- minitap/mobile_use/agents/executor/tool_node.py +152 -0
- minitap/mobile_use/agents/hopper/hopper.md +15 -0
- minitap/mobile_use/agents/hopper/hopper.py +44 -0
- minitap/mobile_use/agents/orchestrator/human.md +12 -0
- minitap/mobile_use/agents/orchestrator/orchestrator.md +21 -0
- minitap/mobile_use/agents/orchestrator/orchestrator.py +134 -0
- minitap/mobile_use/agents/orchestrator/types.py +11 -0
- minitap/mobile_use/agents/outputter/human.md +25 -0
- minitap/mobile_use/agents/outputter/outputter.py +85 -0
- minitap/mobile_use/agents/outputter/test_outputter.py +167 -0
- minitap/mobile_use/agents/planner/human.md +14 -0
- minitap/mobile_use/agents/planner/planner.md +126 -0
- minitap/mobile_use/agents/planner/planner.py +101 -0
- minitap/mobile_use/agents/planner/types.py +51 -0
- minitap/mobile_use/agents/planner/utils.py +70 -0
- minitap/mobile_use/agents/summarizer/summarizer.py +35 -0
- minitap/mobile_use/agents/video_analyzer/__init__.py +5 -0
- minitap/mobile_use/agents/video_analyzer/human.md +5 -0
- minitap/mobile_use/agents/video_analyzer/video_analyzer.md +37 -0
- minitap/mobile_use/agents/video_analyzer/video_analyzer.py +111 -0
- minitap/mobile_use/clients/browserstack_client.py +477 -0
- minitap/mobile_use/clients/idb_client.py +429 -0
- minitap/mobile_use/clients/ios_client.py +332 -0
- minitap/mobile_use/clients/ios_client_config.py +141 -0
- minitap/mobile_use/clients/ui_automator_client.py +330 -0
- minitap/mobile_use/clients/wda_client.py +526 -0
- minitap/mobile_use/clients/wda_lifecycle.py +367 -0
- minitap/mobile_use/config.py +413 -0
- minitap/mobile_use/constants.py +3 -0
- minitap/mobile_use/context.py +106 -0
- minitap/mobile_use/controllers/__init__.py +0 -0
- minitap/mobile_use/controllers/android_controller.py +524 -0
- minitap/mobile_use/controllers/controller_factory.py +46 -0
- minitap/mobile_use/controllers/device_controller.py +182 -0
- minitap/mobile_use/controllers/ios_controller.py +436 -0
- minitap/mobile_use/controllers/platform_specific_commands_controller.py +199 -0
- minitap/mobile_use/controllers/types.py +106 -0
- minitap/mobile_use/controllers/unified_controller.py +193 -0
- minitap/mobile_use/graph/graph.py +160 -0
- minitap/mobile_use/graph/state.py +115 -0
- minitap/mobile_use/main.py +309 -0
- minitap/mobile_use/sdk/__init__.py +12 -0
- minitap/mobile_use/sdk/agent.py +1294 -0
- minitap/mobile_use/sdk/builders/__init__.py +10 -0
- minitap/mobile_use/sdk/builders/agent_config_builder.py +307 -0
- minitap/mobile_use/sdk/builders/index.py +15 -0
- minitap/mobile_use/sdk/builders/task_request_builder.py +236 -0
- minitap/mobile_use/sdk/constants.py +1 -0
- minitap/mobile_use/sdk/examples/README.md +83 -0
- minitap/mobile_use/sdk/examples/__init__.py +1 -0
- minitap/mobile_use/sdk/examples/app_lock_messaging.py +54 -0
- minitap/mobile_use/sdk/examples/platform_manual_task_example.py +67 -0
- minitap/mobile_use/sdk/examples/platform_minimal_example.py +48 -0
- minitap/mobile_use/sdk/examples/simple_photo_organizer.py +76 -0
- minitap/mobile_use/sdk/examples/smart_notification_assistant.py +225 -0
- minitap/mobile_use/sdk/examples/video_transcription_example.py +117 -0
- minitap/mobile_use/sdk/services/cloud_mobile.py +656 -0
- minitap/mobile_use/sdk/services/platform.py +434 -0
- minitap/mobile_use/sdk/types/__init__.py +51 -0
- minitap/mobile_use/sdk/types/agent.py +84 -0
- minitap/mobile_use/sdk/types/exceptions.py +138 -0
- minitap/mobile_use/sdk/types/platform.py +183 -0
- minitap/mobile_use/sdk/types/task.py +269 -0
- minitap/mobile_use/sdk/utils.py +29 -0
- minitap/mobile_use/services/accessibility.py +100 -0
- minitap/mobile_use/services/llm.py +247 -0
- minitap/mobile_use/services/telemetry.py +421 -0
- minitap/mobile_use/tools/index.py +67 -0
- minitap/mobile_use/tools/mobile/back.py +52 -0
- minitap/mobile_use/tools/mobile/erase_one_char.py +56 -0
- minitap/mobile_use/tools/mobile/focus_and_clear_text.py +317 -0
- minitap/mobile_use/tools/mobile/focus_and_input_text.py +153 -0
- minitap/mobile_use/tools/mobile/launch_app.py +86 -0
- minitap/mobile_use/tools/mobile/long_press_on.py +169 -0
- minitap/mobile_use/tools/mobile/open_link.py +62 -0
- minitap/mobile_use/tools/mobile/press_key.py +83 -0
- minitap/mobile_use/tools/mobile/stop_app.py +62 -0
- minitap/mobile_use/tools/mobile/swipe.py +156 -0
- minitap/mobile_use/tools/mobile/tap.py +154 -0
- minitap/mobile_use/tools/mobile/video_recording.py +177 -0
- minitap/mobile_use/tools/mobile/wait_for_delay.py +81 -0
- minitap/mobile_use/tools/scratchpad.py +147 -0
- minitap/mobile_use/tools/test_utils.py +413 -0
- minitap/mobile_use/tools/tool_wrapper.py +16 -0
- minitap/mobile_use/tools/types.py +35 -0
- minitap/mobile_use/tools/utils.py +336 -0
- minitap/mobile_use/utils/app_launch_utils.py +173 -0
- minitap/mobile_use/utils/cli_helpers.py +37 -0
- minitap/mobile_use/utils/cli_selection.py +143 -0
- minitap/mobile_use/utils/conversations.py +31 -0
- minitap/mobile_use/utils/decorators.py +124 -0
- minitap/mobile_use/utils/errors.py +6 -0
- minitap/mobile_use/utils/file.py +13 -0
- minitap/mobile_use/utils/logger.py +183 -0
- minitap/mobile_use/utils/media.py +186 -0
- minitap/mobile_use/utils/recorder.py +52 -0
- minitap/mobile_use/utils/requests_utils.py +37 -0
- minitap/mobile_use/utils/shell_utils.py +20 -0
- minitap/mobile_use/utils/test_ui_hierarchy.py +178 -0
- minitap/mobile_use/utils/time.py +6 -0
- minitap/mobile_use/utils/ui_hierarchy.py +132 -0
- minitap/mobile_use/utils/video.py +281 -0
- minitap_mobile_use-3.3.0.dist-info/METADATA +329 -0
- minitap_mobile_use-3.3.0.dist-info/RECORD +115 -0
- minitap_mobile_use-3.3.0.dist-info/WHEEL +4 -0
- minitap_mobile_use-3.3.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Video recording tools for mobile devices.
|
|
3
|
+
|
|
4
|
+
Provides start/stop video recording tools that delegate to platform-specific
|
|
5
|
+
controllers (AndroidDeviceController, iOSDeviceController).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import shutil
|
|
9
|
+
from typing import Annotated
|
|
10
|
+
|
|
11
|
+
from langchain_core.messages import ToolMessage
|
|
12
|
+
from langchain_core.tools import tool
|
|
13
|
+
from langchain_core.tools.base import BaseTool, InjectedToolCallId
|
|
14
|
+
from langgraph.prebuilt import InjectedState
|
|
15
|
+
from langgraph.types import Command
|
|
16
|
+
|
|
17
|
+
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
18
|
+
from minitap.mobile_use.context import DevicePlatform, MobileUseContext
|
|
19
|
+
from minitap.mobile_use.controllers.controller_factory import get_controller
|
|
20
|
+
from minitap.mobile_use.graph.state import State
|
|
21
|
+
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
22
|
+
from minitap.mobile_use.utils.logger import get_logger
|
|
23
|
+
from minitap.mobile_use.utils.video import DEFAULT_MAX_DURATION_SECONDS
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_start_video_recording_tool(ctx: MobileUseContext) -> BaseTool:
|
|
29
|
+
@tool
|
|
30
|
+
async def start_video_recording(
|
|
31
|
+
agent_thought: str,
|
|
32
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
33
|
+
state: Annotated[State, InjectedState],
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Starts a background screen recording on the mobile device.
|
|
37
|
+
Recording continues until stop_video_recording is called.
|
|
38
|
+
Max duration: 3 min (Android) / 15 min (iOS). Audio is not captured.
|
|
39
|
+
"""
|
|
40
|
+
platform = ctx.device.mobile_platform
|
|
41
|
+
controller = get_controller(ctx)
|
|
42
|
+
|
|
43
|
+
if platform in (DevicePlatform.ANDROID, DevicePlatform.IOS):
|
|
44
|
+
result = await controller.start_video_recording(DEFAULT_MAX_DURATION_SECONDS)
|
|
45
|
+
else:
|
|
46
|
+
from minitap.mobile_use.utils.video import VideoRecordingResult
|
|
47
|
+
|
|
48
|
+
result = VideoRecordingResult(
|
|
49
|
+
success=False,
|
|
50
|
+
message=f"Unsupported platform: {platform}",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if result.success:
|
|
54
|
+
agent_outcome = start_video_recording_wrapper.on_success_fn(result.message)
|
|
55
|
+
else:
|
|
56
|
+
agent_outcome = start_video_recording_wrapper.on_failure_fn(result.message)
|
|
57
|
+
|
|
58
|
+
tool_message = ToolMessage(
|
|
59
|
+
tool_call_id=tool_call_id,
|
|
60
|
+
content=agent_outcome,
|
|
61
|
+
status="success" if result.success else "error",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return Command(
|
|
65
|
+
update=await state.asanitize_update(
|
|
66
|
+
ctx=ctx,
|
|
67
|
+
update={
|
|
68
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
69
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
70
|
+
},
|
|
71
|
+
agent="executor",
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return start_video_recording
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_stop_video_recording_tool(ctx: MobileUseContext) -> BaseTool:
|
|
79
|
+
from minitap.mobile_use.agents.video_analyzer.video_analyzer import analyze_video
|
|
80
|
+
|
|
81
|
+
@tool
|
|
82
|
+
async def stop_video_recording(
|
|
83
|
+
agent_thought: str,
|
|
84
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
85
|
+
state: Annotated[State, InjectedState],
|
|
86
|
+
prompt: str = "Describe what happened in the video.",
|
|
87
|
+
):
|
|
88
|
+
"""
|
|
89
|
+
Stops the current screen recording and analyzes the video content.
|
|
90
|
+
Use `prompt` to specify what to extract (e.g., "What happens after each 10s of the video?").
|
|
91
|
+
"""
|
|
92
|
+
platform = ctx.device.mobile_platform
|
|
93
|
+
controller = get_controller(ctx)
|
|
94
|
+
|
|
95
|
+
if platform in (DevicePlatform.ANDROID, DevicePlatform.IOS):
|
|
96
|
+
result = await controller.stop_video_recording()
|
|
97
|
+
else:
|
|
98
|
+
from minitap.mobile_use.utils.video import VideoRecordingResult
|
|
99
|
+
|
|
100
|
+
result = VideoRecordingResult(
|
|
101
|
+
success=False,
|
|
102
|
+
message=f"Unsupported platform: {platform}",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if not result.success or result.video_path is None:
|
|
106
|
+
agent_outcome = stop_video_recording_wrapper.on_failure_fn(result.message)
|
|
107
|
+
tool_message = ToolMessage(
|
|
108
|
+
tool_call_id=tool_call_id,
|
|
109
|
+
content=agent_outcome,
|
|
110
|
+
status="error",
|
|
111
|
+
)
|
|
112
|
+
return Command(
|
|
113
|
+
update=await state.asanitize_update(
|
|
114
|
+
ctx=ctx,
|
|
115
|
+
update={
|
|
116
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
117
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
118
|
+
},
|
|
119
|
+
agent="executor",
|
|
120
|
+
),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
video_path = result.video_path
|
|
124
|
+
try:
|
|
125
|
+
analysis_result = await analyze_video(
|
|
126
|
+
ctx=ctx,
|
|
127
|
+
video_path=video_path,
|
|
128
|
+
prompt=prompt,
|
|
129
|
+
)
|
|
130
|
+
agent_outcome = stop_video_recording_wrapper.on_success_fn(analysis_result)
|
|
131
|
+
status = "success"
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Video analysis failed: {e}")
|
|
134
|
+
agent_outcome = stop_video_recording_wrapper.on_failure_fn(
|
|
135
|
+
f"Recording stopped but analysis failed: {e}"
|
|
136
|
+
)
|
|
137
|
+
status = "error"
|
|
138
|
+
finally:
|
|
139
|
+
try:
|
|
140
|
+
if video_path and video_path.exists():
|
|
141
|
+
video_path.unlink()
|
|
142
|
+
if video_path.parent.exists():
|
|
143
|
+
shutil.rmtree(video_path.parent)
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
tool_message = ToolMessage(
|
|
148
|
+
tool_call_id=tool_call_id,
|
|
149
|
+
content=agent_outcome,
|
|
150
|
+
status=status,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return Command(
|
|
154
|
+
update=await state.asanitize_update(
|
|
155
|
+
ctx=ctx,
|
|
156
|
+
update={
|
|
157
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
158
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
159
|
+
},
|
|
160
|
+
agent="executor",
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return stop_video_recording
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
start_video_recording_wrapper = ToolWrapper(
|
|
168
|
+
tool_fn_getter=get_start_video_recording_tool,
|
|
169
|
+
on_success_fn=lambda message: f"Video recording started successfully. {message}",
|
|
170
|
+
on_failure_fn=lambda message: f"Failed to start video recording: {message}",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
stop_video_recording_wrapper = ToolWrapper(
|
|
174
|
+
tool_fn_getter=get_stop_video_recording_tool,
|
|
175
|
+
on_success_fn=lambda analysis: f"Video stopped successfully. Analysis result:\n{analysis}",
|
|
176
|
+
on_failure_fn=lambda message: f"Video recording/analysis failed: {message}",
|
|
177
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
from langchain_core.messages import ToolMessage
|
|
5
|
+
from langchain_core.tools import tool
|
|
6
|
+
from langchain_core.tools.base import InjectedToolCallId
|
|
7
|
+
from langgraph.prebuilt import InjectedState
|
|
8
|
+
from langgraph.types import Command
|
|
9
|
+
|
|
10
|
+
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
11
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
12
|
+
from minitap.mobile_use.graph.state import State
|
|
13
|
+
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
14
|
+
|
|
15
|
+
MAX_DELAY_MS = 60000
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_wait_for_delay_tool(ctx: MobileUseContext):
|
|
19
|
+
@tool
|
|
20
|
+
async def wait_for_delay(
|
|
21
|
+
agent_thought: str,
|
|
22
|
+
time_in_ms: int,
|
|
23
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
24
|
+
state: Annotated[State, InjectedState],
|
|
25
|
+
) -> Command:
|
|
26
|
+
"""
|
|
27
|
+
Wait for a delay in milliseconds.
|
|
28
|
+
|
|
29
|
+
This tool pauses execution for a specified number of milliseconds.
|
|
30
|
+
Use this when you need to introduce a controlled delay to allow the UI
|
|
31
|
+
to update after an action, regardless of whether an animation is playing.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
time_in_ms: The number of milliseconds to wait. (capped at 60 seconds)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
- wait_for_delay with time_in_ms=1000 (waits 1 second)
|
|
39
|
+
- wait_for_delay with time_in_ms=500 (waits 0.5 seconds)
|
|
40
|
+
"""
|
|
41
|
+
if time_in_ms < 0:
|
|
42
|
+
time_in_ms = 1000
|
|
43
|
+
if time_in_ms > MAX_DELAY_MS:
|
|
44
|
+
time_in_ms = MAX_DELAY_MS
|
|
45
|
+
try:
|
|
46
|
+
time.sleep(time_in_ms / 1000)
|
|
47
|
+
output = None
|
|
48
|
+
has_failed = False
|
|
49
|
+
except Exception as e:
|
|
50
|
+
output = str(e)
|
|
51
|
+
has_failed = True
|
|
52
|
+
agent_outcome = (
|
|
53
|
+
wait_for_delay_wrapper.on_failure_fn()
|
|
54
|
+
if has_failed
|
|
55
|
+
else wait_for_delay_wrapper.on_success_fn(time_in_ms)
|
|
56
|
+
)
|
|
57
|
+
tool_message = ToolMessage(
|
|
58
|
+
tool_call_id=tool_call_id,
|
|
59
|
+
content=agent_outcome,
|
|
60
|
+
additional_kwargs={"error": output} if has_failed else {},
|
|
61
|
+
status="error" if has_failed else "success",
|
|
62
|
+
)
|
|
63
|
+
return Command(
|
|
64
|
+
update=await state.asanitize_update(
|
|
65
|
+
ctx=ctx,
|
|
66
|
+
update={
|
|
67
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
68
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
69
|
+
},
|
|
70
|
+
agent="executor",
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return wait_for_delay
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
wait_for_delay_wrapper = ToolWrapper(
|
|
78
|
+
tool_fn_getter=get_wait_for_delay_tool,
|
|
79
|
+
on_success_fn=lambda delay: f"Successfully waited for {delay} milliseconds.",
|
|
80
|
+
on_failure_fn=lambda: "Failed to wait for delay.",
|
|
81
|
+
)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import ToolMessage
|
|
4
|
+
from langchain_core.tools import tool
|
|
5
|
+
from langchain_core.tools.base import BaseTool, InjectedToolCallId
|
|
6
|
+
from langgraph.prebuilt import InjectedState
|
|
7
|
+
from langgraph.types import Command
|
|
8
|
+
|
|
9
|
+
from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
|
|
10
|
+
from minitap.mobile_use.context import MobileUseContext
|
|
11
|
+
from minitap.mobile_use.graph.state import State
|
|
12
|
+
from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
|
|
13
|
+
from minitap.mobile_use.utils.logger import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_save_note_tool(ctx: MobileUseContext) -> BaseTool:
|
|
19
|
+
@tool
|
|
20
|
+
async def save_note(
|
|
21
|
+
agent_thought: str,
|
|
22
|
+
key: str,
|
|
23
|
+
content: str,
|
|
24
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
25
|
+
state: Annotated[State, InjectedState],
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Saves a text note to persistent memory with the given key.
|
|
29
|
+
If the key already exists, it will be overwritten.
|
|
30
|
+
"""
|
|
31
|
+
updated_scratchpad = {**state.scratchpad, key: content}
|
|
32
|
+
|
|
33
|
+
agent_outcome = save_note_wrapper.on_success_fn(key)
|
|
34
|
+
|
|
35
|
+
tool_message = ToolMessage(
|
|
36
|
+
tool_call_id=tool_call_id,
|
|
37
|
+
content=agent_outcome,
|
|
38
|
+
status="success",
|
|
39
|
+
)
|
|
40
|
+
return Command(
|
|
41
|
+
update=await state.asanitize_update(
|
|
42
|
+
ctx=ctx,
|
|
43
|
+
update={
|
|
44
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
45
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
46
|
+
"scratchpad": updated_scratchpad,
|
|
47
|
+
},
|
|
48
|
+
agent="executor",
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return save_note
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_read_note_tool(ctx: MobileUseContext) -> BaseTool:
|
|
56
|
+
@tool
|
|
57
|
+
async def read_note(
|
|
58
|
+
agent_thought: str,
|
|
59
|
+
key: str,
|
|
60
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
61
|
+
state: Annotated[State, InjectedState],
|
|
62
|
+
):
|
|
63
|
+
"""
|
|
64
|
+
Reads a previously saved note from persistent memory by its key.
|
|
65
|
+
"""
|
|
66
|
+
content = state.scratchpad.get(key)
|
|
67
|
+
|
|
68
|
+
if content is not None:
|
|
69
|
+
agent_outcome = read_note_wrapper.on_success_fn(key, content)
|
|
70
|
+
status = "success"
|
|
71
|
+
else:
|
|
72
|
+
agent_outcome = read_note_wrapper.on_failure_fn(key)
|
|
73
|
+
status = "error"
|
|
74
|
+
|
|
75
|
+
tool_message = ToolMessage(
|
|
76
|
+
tool_call_id=tool_call_id,
|
|
77
|
+
content=agent_outcome,
|
|
78
|
+
status=status,
|
|
79
|
+
)
|
|
80
|
+
return Command(
|
|
81
|
+
update=await state.asanitize_update(
|
|
82
|
+
ctx=ctx,
|
|
83
|
+
update={
|
|
84
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
85
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
86
|
+
},
|
|
87
|
+
agent="executor",
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return read_note
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_list_notes_tool(ctx: MobileUseContext) -> BaseTool:
|
|
95
|
+
@tool
|
|
96
|
+
async def list_notes(
|
|
97
|
+
agent_thought: str,
|
|
98
|
+
tool_call_id: Annotated[str, InjectedToolCallId],
|
|
99
|
+
state: Annotated[State, InjectedState],
|
|
100
|
+
):
|
|
101
|
+
"""
|
|
102
|
+
Lists all note keys currently stored in persistent memory.
|
|
103
|
+
"""
|
|
104
|
+
keys = list(state.scratchpad.keys())
|
|
105
|
+
|
|
106
|
+
agent_outcome = list_notes_wrapper.on_success_fn(keys)
|
|
107
|
+
|
|
108
|
+
tool_message = ToolMessage(
|
|
109
|
+
tool_call_id=tool_call_id,
|
|
110
|
+
content=agent_outcome,
|
|
111
|
+
status="success",
|
|
112
|
+
)
|
|
113
|
+
return Command(
|
|
114
|
+
update=await state.asanitize_update(
|
|
115
|
+
ctx=ctx,
|
|
116
|
+
update={
|
|
117
|
+
"agents_thoughts": [agent_thought, agent_outcome],
|
|
118
|
+
EXECUTOR_MESSAGES_KEY: [tool_message],
|
|
119
|
+
},
|
|
120
|
+
agent="executor",
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return list_notes
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
save_note_wrapper = ToolWrapper(
|
|
128
|
+
tool_fn_getter=get_save_note_tool,
|
|
129
|
+
on_success_fn=lambda key: (f"Successfully saved note '{key}'."),
|
|
130
|
+
on_failure_fn=lambda key: f"Failed to save note '{key}'.",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
read_note_wrapper = ToolWrapper(
|
|
134
|
+
tool_fn_getter=get_read_note_tool,
|
|
135
|
+
on_success_fn=lambda key, content: (
|
|
136
|
+
f"Successfully read note '{key}'. '{key}' note full content: {content}"
|
|
137
|
+
),
|
|
138
|
+
on_failure_fn=lambda key: f"Note '{key}' not found in scratchpad.",
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
list_notes_wrapper = ToolWrapper(
|
|
142
|
+
tool_fn_getter=get_list_notes_tool,
|
|
143
|
+
on_success_fn=lambda keys: (
|
|
144
|
+
f"Here are all the note keys: {keys}" if keys else "No notes saved yet."
|
|
145
|
+
),
|
|
146
|
+
on_failure_fn=lambda: "Failed to list notes.",
|
|
147
|
+
)
|