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.

Files changed (95) hide show
  1. minitap/mobile_use/__init__.py +0 -0
  2. minitap/mobile_use/agents/contextor/contextor.py +42 -0
  3. minitap/mobile_use/agents/cortex/cortex.md +93 -0
  4. minitap/mobile_use/agents/cortex/cortex.py +107 -0
  5. minitap/mobile_use/agents/cortex/types.py +11 -0
  6. minitap/mobile_use/agents/executor/executor.md +73 -0
  7. minitap/mobile_use/agents/executor/executor.py +84 -0
  8. minitap/mobile_use/agents/executor/executor_context_cleaner.py +27 -0
  9. minitap/mobile_use/agents/executor/utils.py +11 -0
  10. minitap/mobile_use/agents/hopper/hopper.md +13 -0
  11. minitap/mobile_use/agents/hopper/hopper.py +45 -0
  12. minitap/mobile_use/agents/orchestrator/human.md +13 -0
  13. minitap/mobile_use/agents/orchestrator/orchestrator.md +18 -0
  14. minitap/mobile_use/agents/orchestrator/orchestrator.py +114 -0
  15. minitap/mobile_use/agents/orchestrator/types.py +14 -0
  16. minitap/mobile_use/agents/outputter/human.md +25 -0
  17. minitap/mobile_use/agents/outputter/outputter.py +75 -0
  18. minitap/mobile_use/agents/outputter/test_outputter.py +107 -0
  19. minitap/mobile_use/agents/planner/human.md +12 -0
  20. minitap/mobile_use/agents/planner/planner.md +64 -0
  21. minitap/mobile_use/agents/planner/planner.py +64 -0
  22. minitap/mobile_use/agents/planner/types.py +44 -0
  23. minitap/mobile_use/agents/planner/utils.py +45 -0
  24. minitap/mobile_use/agents/summarizer/summarizer.py +34 -0
  25. minitap/mobile_use/clients/device_hardware_client.py +23 -0
  26. minitap/mobile_use/clients/ios_client.py +44 -0
  27. minitap/mobile_use/clients/screen_api_client.py +53 -0
  28. minitap/mobile_use/config.py +285 -0
  29. minitap/mobile_use/constants.py +2 -0
  30. minitap/mobile_use/context.py +65 -0
  31. minitap/mobile_use/controllers/__init__.py +0 -0
  32. minitap/mobile_use/controllers/mobile_command_controller.py +379 -0
  33. minitap/mobile_use/controllers/platform_specific_commands_controller.py +74 -0
  34. minitap/mobile_use/graph/graph.py +149 -0
  35. minitap/mobile_use/graph/state.py +73 -0
  36. minitap/mobile_use/main.py +122 -0
  37. minitap/mobile_use/sdk/__init__.py +12 -0
  38. minitap/mobile_use/sdk/agent.py +524 -0
  39. minitap/mobile_use/sdk/builders/__init__.py +10 -0
  40. minitap/mobile_use/sdk/builders/agent_config_builder.py +213 -0
  41. minitap/mobile_use/sdk/builders/index.py +15 -0
  42. minitap/mobile_use/sdk/builders/task_request_builder.py +218 -0
  43. minitap/mobile_use/sdk/constants.py +14 -0
  44. minitap/mobile_use/sdk/examples/README.md +45 -0
  45. minitap/mobile_use/sdk/examples/__init__.py +1 -0
  46. minitap/mobile_use/sdk/examples/simple_photo_organizer.py +76 -0
  47. minitap/mobile_use/sdk/examples/smart_notification_assistant.py +177 -0
  48. minitap/mobile_use/sdk/types/__init__.py +49 -0
  49. minitap/mobile_use/sdk/types/agent.py +73 -0
  50. minitap/mobile_use/sdk/types/exceptions.py +74 -0
  51. minitap/mobile_use/sdk/types/task.py +191 -0
  52. minitap/mobile_use/sdk/utils.py +28 -0
  53. minitap/mobile_use/servers/config.py +19 -0
  54. minitap/mobile_use/servers/device_hardware_bridge.py +212 -0
  55. minitap/mobile_use/servers/device_screen_api.py +143 -0
  56. minitap/mobile_use/servers/start_servers.py +151 -0
  57. minitap/mobile_use/servers/stop_servers.py +215 -0
  58. minitap/mobile_use/servers/utils.py +11 -0
  59. minitap/mobile_use/services/accessibility.py +100 -0
  60. minitap/mobile_use/services/llm.py +143 -0
  61. minitap/mobile_use/tools/index.py +54 -0
  62. minitap/mobile_use/tools/mobile/back.py +52 -0
  63. minitap/mobile_use/tools/mobile/copy_text_from.py +77 -0
  64. minitap/mobile_use/tools/mobile/erase_text.py +124 -0
  65. minitap/mobile_use/tools/mobile/input_text.py +74 -0
  66. minitap/mobile_use/tools/mobile/launch_app.py +59 -0
  67. minitap/mobile_use/tools/mobile/list_packages.py +78 -0
  68. minitap/mobile_use/tools/mobile/long_press_on.py +62 -0
  69. minitap/mobile_use/tools/mobile/open_link.py +59 -0
  70. minitap/mobile_use/tools/mobile/paste_text.py +66 -0
  71. minitap/mobile_use/tools/mobile/press_key.py +58 -0
  72. minitap/mobile_use/tools/mobile/run_flow.py +57 -0
  73. minitap/mobile_use/tools/mobile/stop_app.py +58 -0
  74. minitap/mobile_use/tools/mobile/swipe.py +56 -0
  75. minitap/mobile_use/tools/mobile/take_screenshot.py +70 -0
  76. minitap/mobile_use/tools/mobile/tap.py +66 -0
  77. minitap/mobile_use/tools/mobile/wait_for_animation_to_end.py +68 -0
  78. minitap/mobile_use/tools/tool_wrapper.py +33 -0
  79. minitap/mobile_use/utils/cli_helpers.py +40 -0
  80. minitap/mobile_use/utils/cli_selection.py +144 -0
  81. minitap/mobile_use/utils/conversations.py +31 -0
  82. minitap/mobile_use/utils/decorators.py +123 -0
  83. minitap/mobile_use/utils/errors.py +6 -0
  84. minitap/mobile_use/utils/file.py +13 -0
  85. minitap/mobile_use/utils/logger.py +184 -0
  86. minitap/mobile_use/utils/media.py +73 -0
  87. minitap/mobile_use/utils/recorder.py +55 -0
  88. minitap/mobile_use/utils/requests_utils.py +37 -0
  89. minitap/mobile_use/utils/shell_utils.py +20 -0
  90. minitap/mobile_use/utils/time.py +6 -0
  91. minitap/mobile_use/utils/ui_hierarchy.py +30 -0
  92. minitap_mobile_use-0.0.1.dev0.dist-info/METADATA +274 -0
  93. minitap_mobile_use-0.0.1.dev0.dist-info/RECORD +95 -0
  94. minitap_mobile_use-0.0.1.dev0.dist-info/WHEEL +4 -0
  95. minitap_mobile_use-0.0.1.dev0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,213 @@
1
+ """
2
+ Builder for AgentConfig objects using a fluent interface.
3
+ """
4
+
5
+ from typing import Dict, Optional, List
6
+ import copy
7
+
8
+ from minitap.mobile_use.config import get_default_llm_config
9
+ from minitap.mobile_use.sdk.constants import (
10
+ DEFAULT_HW_BRIDGE_BASE_URL,
11
+ DEFAULT_PROFILE_NAME,
12
+ DEFAULT_SCREEN_API_BASE_URL,
13
+ )
14
+ from minitap.mobile_use.sdk.types.agent import ApiBaseUrl, AgentConfig, ServerConfig
15
+ from minitap.mobile_use.sdk.types.agent import AgentProfile
16
+ from minitap.mobile_use.sdk.types.task import TaskRequestCommon
17
+ from minitap.mobile_use.context import DevicePlatform
18
+
19
+
20
+ class AgentConfigBuilder:
21
+ """
22
+ Builder class providing a fluent interface for creating AgentConfig objects.
23
+
24
+ This builder allows for step-by-step construction of an AgentConfig with
25
+ clear methods that make the configuration process intuitive and type-safe.
26
+
27
+ Examples:
28
+ >>> builder = AgentConfigBuilder()
29
+ >>> config = (builder
30
+ ... .add_profile(AgentProfile(name="HighReasoning", llm_config=LLMConfig(...)))
31
+ ... .add_profile(AgentProfile(name="LowReasoning", llm_config=LLMConfig(...)))
32
+ ... .for_device(DevicePlatform.ANDROID, "device123")
33
+ ... .with_default_task_config(TaskRequestCommon(max_steps=30))
34
+ ... .with_default_profile("HighReasoning")
35
+ ... .build()
36
+ ... )
37
+ """
38
+
39
+ def __init__(self):
40
+ """Initialize an empty AgentConfigBuilder."""
41
+ self._agent_profiles: Dict[str, AgentProfile] = {}
42
+ self._task_request_defaults: Optional[TaskRequestCommon] = None
43
+ self._default_profile: Optional[str | AgentProfile] = None
44
+ self._device_id: Optional[str] = None
45
+ self._device_platform: Optional[DevicePlatform] = None
46
+ self._servers: ServerConfig = get_default_servers()
47
+
48
+ def add_profile(self, profile: AgentProfile) -> "AgentConfigBuilder":
49
+ """
50
+ Add an agent profile to the mobile-use agent.
51
+
52
+ Args:
53
+ profile: The agent profile to add
54
+ """
55
+ self._agent_profiles[profile.name] = profile
56
+ profile.llm_config.validate_providers()
57
+ return self
58
+
59
+ def add_profiles(self, profiles: List[AgentProfile]) -> "AgentConfigBuilder":
60
+ """
61
+ Add multiple agent profiles to the mobile-use agent.
62
+
63
+ Args:
64
+ profiles: List of agent profiles to add
65
+ """
66
+ for profile in profiles:
67
+ self.add_profile(profile=profile)
68
+ profile.llm_config.validate_providers()
69
+ return self
70
+
71
+ def with_default_profile(self, profile: str | AgentProfile) -> "AgentConfigBuilder":
72
+ """
73
+ Set the default agent profile used for tasks.
74
+
75
+ Args:
76
+ profile: The name or instance of the default agent profile
77
+ """
78
+ self._default_profile = profile
79
+ return self
80
+
81
+ def for_device(
82
+ self,
83
+ platform: DevicePlatform,
84
+ device_id: str,
85
+ ) -> "AgentConfigBuilder":
86
+ """
87
+ Configure the mobile-use agent for a specific device.
88
+
89
+ Args:
90
+ platform: The device platform (ANDROID or IOS)
91
+ device_id: The unique identifier for the device
92
+ """
93
+ self._device_id = device_id
94
+ self._device_platform = platform
95
+ return self
96
+
97
+ def with_default_task_config(self, config: TaskRequestCommon) -> "AgentConfigBuilder":
98
+ """
99
+ Set the default task configuration.
100
+
101
+ Args:
102
+ config: The task configuration to use as default
103
+ """
104
+ self._task_request_defaults = copy.deepcopy(config)
105
+ return self
106
+
107
+ def with_hw_bridge_base_url(self, url: str | ApiBaseUrl) -> "AgentConfigBuilder":
108
+ """
109
+ Set the base URL for the device HW bridge API.
110
+
111
+ Args:
112
+ url: The base URL for the HW bridge API
113
+ """
114
+ if isinstance(url, str):
115
+ url = ApiBaseUrl.from_url(url)
116
+ self._servers.hw_bridge_base_url = url
117
+ return self
118
+
119
+ def with_screen_api_base_url(self, url: str | ApiBaseUrl) -> "AgentConfigBuilder":
120
+ """
121
+ Set the base URL for the device screen API.
122
+
123
+ Args:
124
+ url: The base URL for the screen API
125
+ """
126
+ if isinstance(url, str):
127
+ url = ApiBaseUrl.from_url(url)
128
+ self._servers.screen_api_base_url = url
129
+ return self
130
+
131
+ def with_adb_server(self, host: str, port: Optional[int] = None) -> "AgentConfigBuilder":
132
+ """
133
+ Set the ADB server host and port.
134
+
135
+ Args:
136
+ host: The ADB server host
137
+ port: The ADB server port
138
+ """
139
+ self._servers.adb_host = host
140
+ if port is not None:
141
+ self._servers.adb_port = port
142
+ return self
143
+
144
+ def with_servers(self, servers: ServerConfig) -> "AgentConfigBuilder":
145
+ """
146
+ Set the server settings.
147
+
148
+ Args:
149
+ servers: The server settings to use
150
+ """
151
+ self._servers = copy.deepcopy(servers)
152
+ return self
153
+
154
+ def build(self) -> AgentConfig:
155
+ """
156
+ Build the mobile-use AgentConfig object.
157
+
158
+ Args:
159
+ default_profile: Name of the default agent profile to use
160
+
161
+ Returns:
162
+ A configured AgentConfig object
163
+
164
+ Raises:
165
+ ValueError: If default_profile is specified but not found in configured profiles
166
+ """
167
+ nb_profiles = len(self._agent_profiles)
168
+
169
+ if isinstance(self._default_profile, str):
170
+ profile_name = self._default_profile
171
+ default_profile = self._agent_profiles.get(profile_name, None)
172
+ if default_profile is None:
173
+ raise ValueError(f"Profile '{profile_name}' not found in configured agents")
174
+ elif isinstance(self._default_profile, AgentProfile):
175
+ default_profile = self._default_profile
176
+ if default_profile.name not in self._agent_profiles:
177
+ self.add_profile(default_profile)
178
+ elif nb_profiles <= 0:
179
+ default_profile = AgentProfile(
180
+ name=DEFAULT_PROFILE_NAME,
181
+ llm_config=get_default_llm_config(),
182
+ )
183
+ self.add_profile(default_profile)
184
+ elif nb_profiles == 1:
185
+ # Select the only one available
186
+ default_profile = next(iter(self._agent_profiles.values()))
187
+ else:
188
+ available_profiles = ", ".join(self._agent_profiles.keys())
189
+ raise ValueError(
190
+ f"You must call with_default_profile() to select one among: {available_profiles}"
191
+ )
192
+
193
+ return AgentConfig(
194
+ agent_profiles=self._agent_profiles,
195
+ task_request_defaults=self._task_request_defaults or TaskRequestCommon(),
196
+ default_profile=default_profile,
197
+ device_id=self._device_id,
198
+ device_platform=self._device_platform,
199
+ servers=self._servers,
200
+ )
201
+
202
+
203
+ def get_default_agent_config():
204
+ return AgentConfigBuilder().build()
205
+
206
+
207
+ def get_default_servers():
208
+ return ServerConfig(
209
+ hw_bridge_base_url=copy.deepcopy(DEFAULT_HW_BRIDGE_BASE_URL),
210
+ screen_api_base_url=copy.deepcopy(DEFAULT_SCREEN_API_BASE_URL),
211
+ adb_host="localhost",
212
+ adb_port=5037,
213
+ )
@@ -0,0 +1,15 @@
1
+ from minitap.mobile_use.sdk.builders.agent_config_builder import AgentConfigBuilder
2
+ from minitap.mobile_use.sdk.builders.task_request_builder import TaskRequestCommonBuilder
3
+
4
+
5
+ class BuildersWrapper:
6
+ @property
7
+ def AgentConfig(self) -> AgentConfigBuilder:
8
+ return AgentConfigBuilder()
9
+
10
+ @property
11
+ def TaskDefaults(self) -> TaskRequestCommonBuilder:
12
+ return TaskRequestCommonBuilder()
13
+
14
+
15
+ Builders = BuildersWrapper()
@@ -0,0 +1,218 @@
1
+ """
2
+ Builder for TaskRequest objects using a fluent interface.
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import Generic, Optional, Self, TypeVar, cast
7
+
8
+ from pydantic import BaseModel
9
+
10
+ from minitap.mobile_use.constants import RECURSION_LIMIT
11
+ from minitap.mobile_use.sdk.types.agent import AgentProfile
12
+ from minitap.mobile_use.sdk.types.task import TaskRequest, TaskRequestCommon
13
+
14
+
15
+ TIn = TypeVar("TIn", bound=Optional[BaseModel])
16
+ TOut = TypeVar("TOut", bound=BaseModel)
17
+
18
+
19
+ class TaskRequestCommonBuilder(BaseModel):
20
+ """
21
+ Builder class providing a fluent interface for creating TaskRequestCommon objects.
22
+ """
23
+
24
+ def __init__(self):
25
+ self._max_steps = RECURSION_LIMIT
26
+ self._record_trace = False
27
+ self._trace_path = Path("mobile-use-traces")
28
+ self._llm_output_path: Optional[Path] = None
29
+ self._thoughts_output_path: Optional[Path] = None
30
+
31
+ def with_max_steps(self, max_steps: int) -> Self:
32
+ """
33
+ Set the maximum number of steps the task can take.
34
+
35
+ Args:
36
+ max_steps: Maximum number of steps
37
+ """
38
+ self._max_steps = max_steps
39
+ return self
40
+
41
+ def with_trace_recording(self, enabled: bool = True, path: Optional[str] = None) -> Self:
42
+ """
43
+ Configure trace recording for the task.
44
+
45
+ Traces record screenshots and actions during execution.
46
+
47
+ Args:
48
+ enabled: Whether to enable trace recording
49
+ path: Directory path where traces should be saved
50
+ """
51
+ self._record_trace = enabled
52
+ if enabled and path:
53
+ self._trace_path = Path(path)
54
+ return self
55
+
56
+ def with_llm_output_saving(self, path: str) -> Self:
57
+ """
58
+ Configure LLM output saving for the task.
59
+
60
+ Args:
61
+ path: Path where to save the LLM output message
62
+ """
63
+ self._llm_output_path = Path(path)
64
+ return self
65
+
66
+ def with_thoughts_output_saving(self, path: str) -> Self:
67
+ """
68
+ Configure thoughts output saving for the task.
69
+
70
+ Args:
71
+ path: Path where to save the thoughts output message
72
+ """
73
+ self._thoughts_output_path = Path(path)
74
+ return self
75
+
76
+ def build(self) -> TaskRequestCommon:
77
+ """
78
+ Build the TaskRequestCommon object.
79
+
80
+ Returns:
81
+ A configured TaskRequestCommon object
82
+
83
+ Raises:
84
+ ValueError: If required fields are missing
85
+ """
86
+ return TaskRequestCommon(
87
+ max_steps=self._max_steps,
88
+ record_trace=self._record_trace,
89
+ trace_path=self._trace_path,
90
+ llm_output_path=self._llm_output_path,
91
+ thoughts_output_path=self._thoughts_output_path,
92
+ )
93
+
94
+
95
+ class TaskRequestBuilder(TaskRequestCommonBuilder, Generic[TIn]):
96
+ """
97
+ Builder class providing a fluent interface for creating TaskRequest objects.
98
+
99
+ This builder allows for step-by-step construction of a TaskRequest with
100
+ clear methods that make the configuration process intuitive and type-safe.
101
+
102
+ Examples:
103
+ >>> builder = TaskRequestBuilder[None](goal="Open Gmail and check unread emails")
104
+ >>> task_request = (
105
+ ... builder
106
+ ... .with_max_steps(30)
107
+ ... .using_profile("LowReasoning")
108
+ ... .with_output_description("A list of email subjects and senders")
109
+ ... .build()
110
+ ... )
111
+ """
112
+
113
+ def __init__(self, goal: str):
114
+ """Initialize an empty TaskRequestBuilder."""
115
+ super().__init__()
116
+ self._goal = goal
117
+ self._profile: Optional[str | AgentProfile] = None
118
+ self._name: Optional[str] = None
119
+ self._output_description = None
120
+ self._output_format: Optional[type[TIn]] = None
121
+
122
+ @classmethod
123
+ def from_common(cls, goal: str, common: TaskRequestCommon):
124
+ res = cls(goal=goal)
125
+ res._max_steps = common.max_steps
126
+ res._record_trace = common.record_trace
127
+ res._trace_path = common.trace_path
128
+ res._llm_output_path = common.llm_output_path
129
+ res._thoughts_output_path = common.thoughts_output_path
130
+ return res
131
+
132
+ def using_profile(self, profile: str | AgentProfile) -> "TaskRequestBuilder[TIn]":
133
+ """
134
+ Set the agent profile for executing the task.
135
+
136
+ Args:
137
+ profile: The agent profile to use
138
+ """
139
+ self._profile = profile
140
+ return self
141
+
142
+ def with_name(self, name: str) -> "TaskRequestBuilder[TIn]":
143
+ """
144
+ Set the name of the task - useful when recording traces.
145
+ Otherwise, a random name will be generated.
146
+
147
+ Args:
148
+ name: Name of the task
149
+ """
150
+ self._name = name
151
+ return self
152
+
153
+ def without_llm_output_saving(self) -> Self:
154
+ """
155
+ Disable LLM output saving for the task.
156
+ """
157
+ self._llm_output_path = None
158
+ return self
159
+
160
+ def without_thoughts_output_saving(self):
161
+ """
162
+ Disable thoughts output saving for the task.
163
+ """
164
+ self._thoughts_output_path = None
165
+ return self
166
+
167
+ def with_output_description(self, description: str) -> "TaskRequestBuilder[TIn]":
168
+ """
169
+ Set the description of the expected output format.
170
+ This is especially useful for data extraction tasks.
171
+
172
+ Args:
173
+ description: Description of the expected output format
174
+ """
175
+ self._output_description = description
176
+ return self
177
+
178
+ def with_output_format(self, output_format: type[TOut]) -> "TaskRequestBuilder[TOut]":
179
+ """
180
+ Set the pydantic model for the expected output format.
181
+
182
+ Args:
183
+ output_format: Pydantic model instance defining the output format
184
+ """
185
+ self._output_format = output_format # type: ignore
186
+ return cast(TaskRequestBuilder[TOut], self)
187
+
188
+ def build(self) -> TaskRequest[TIn]:
189
+ """
190
+ Build the TaskRequest object.
191
+
192
+ Returns:
193
+ A configured TaskRequest object
194
+
195
+ Raises:
196
+ ValueError: If required fields are missing
197
+ """
198
+ if not self._goal:
199
+ raise ValueError("Task goal is required")
200
+
201
+ if self._output_format and self._output_description:
202
+ raise ValueError("Output format and description are mutually exclusive")
203
+
204
+ task_request = TaskRequest(
205
+ goal=self._goal,
206
+ profile=self._profile.name
207
+ if isinstance(self._profile, AgentProfile)
208
+ else self._profile,
209
+ task_name=self._name,
210
+ output_description=self._output_description,
211
+ output_format=self._output_format,
212
+ max_steps=self._max_steps,
213
+ record_trace=self._record_trace,
214
+ trace_path=self._trace_path,
215
+ llm_output_path=self._llm_output_path,
216
+ thoughts_output_path=self._thoughts_output_path,
217
+ )
218
+ return task_request
@@ -0,0 +1,14 @@
1
+ from minitap.mobile_use.sdk.types.agent import ApiBaseUrl
2
+
3
+
4
+ DEFAULT_PROFILE_NAME = "default"
5
+ DEFAULT_HW_BRIDGE_BASE_URL = ApiBaseUrl(
6
+ scheme="http",
7
+ host="localhost",
8
+ port=9999,
9
+ )
10
+ DEFAULT_SCREEN_API_BASE_URL = ApiBaseUrl(
11
+ scheme="http",
12
+ host="localhost",
13
+ port=9998,
14
+ )
@@ -0,0 +1,45 @@
1
+ # mobile-use SDK Examples
2
+
3
+ Location: `src/mobile_use/sdk/examples/`
4
+
5
+ Run any example via:
6
+ - `python src/mobile_use/sdk/examples/<filename>.py`
7
+
8
+ ## Practical Automation Examples
9
+
10
+ These examples demonstrate two different ways to use the SDK, each applying an appropriate level of complexity for the task at hand:
11
+
12
+ ### simple_photo_organizer.py - Straightforward Approach
13
+
14
+ Demonstrates the simplest way to use the SDK for quick automation tasks:
15
+
16
+ - **Direct API calls** without builders or complex configuration
17
+ - Creates a photo album and organizes photos from a specific date
18
+ - Uses structured Pydantic output to capture results
19
+
20
+ ### smart_notification_assistant.py - Feature-Rich Approach
21
+
22
+ Showcases more advanced SDK features while remaining practical:
23
+
24
+ - Uses builder pattern for configuring the agent and overriding the default task configurations
25
+ - Implements **multiple specialized agent profiles** for different reasoning tasks:
26
+ - Analyzer profile for detailed inspection of notifications
27
+ - Note taker profile for writing a summary of the notifications
28
+ - Enables **tracing** for debugging and visualization
29
+ - Includes **structured Pydantic models** with enums and nested relationships
30
+ - Demonstrates proper **exception handling** for different error types
31
+ - Shows how to set up task defaults for consistent configuration
32
+
33
+ ## Usage Notes
34
+
35
+ - **Choosing an Approach**: Use the direct approach (like in `simple_photo_organizer.py`) for simple tasks and the builder approach (like in `smart_notification_assistant.py`) when you need more customization.
36
+
37
+ - **Device Detection**: The agent detects the first available device unless you specify one with `AgentConfigBuilder.for_device(...)`.
38
+
39
+ - **Servers**: With default base URLs (`localhost:9998/9999`), the agent starts the servers automatically. When you override URLs, it assumes servers are already running.
40
+
41
+ - **LLM API Keys**: Provide necessary keys (e.g., `OPENAI_API_KEY`) in a `.env` file at repo root; see `mobile_use/config.py`.
42
+
43
+ - **Traces**: When enabled, traces are saved to a specified directory (defaulting to `./mobile-use-traces/`) and can be useful for debugging and visualization.
44
+
45
+ - **Structured Output**: Pydantic models enable type safety when processing task outputs, making it easier to handle and chain results between tasks.
@@ -0,0 +1 @@
1
+ """Example scripts for the mobile-use SDK."""
@@ -0,0 +1,76 @@
1
+ """
2
+ Simple Photo Organizer - Basic SDK Usage Example
3
+
4
+ This example demonstrates a straightforward way to use the mobile-use SDK
5
+ without builders or advanced configuration. It performs a real-world automation task:
6
+ 1. Opens the photo gallery
7
+ 2. Finds photos from a specific date
8
+ 3. Creates an album and moves those photos into it
9
+
10
+ Run:
11
+ - python src/mobile_use/sdk/examples/simple_photo_organizer.py
12
+ """
13
+
14
+ import asyncio
15
+ from datetime import date, timedelta
16
+ from pydantic import BaseModel, Field
17
+ from minitap.mobile_use.sdk import Agent
18
+
19
+
20
+ class PhotosResult(BaseModel):
21
+ """Structured result from photo search."""
22
+
23
+ found_photos: int = Field(..., description="Number of photos found")
24
+ date_range: str = Field(..., description="Date range of photos found")
25
+ album_created: bool = Field(..., description="Whether an album was created")
26
+ album_name: str = Field(..., description="Name of the created album")
27
+ photos_moved: int = Field(0, description="Number of photos moved to the album")
28
+
29
+
30
+ async def main() -> None:
31
+ # Create a simple agent with default configuration
32
+ agent = Agent()
33
+
34
+ try:
35
+ # Initialize agent (finds a device, starts required servers)
36
+ agent.init()
37
+
38
+ # Calculate yesterday's date for the example
39
+ yesterday = date.today() - timedelta(days=1)
40
+ formatted_date = yesterday.strftime("%B %d") # e.g. "August 22"
41
+
42
+ print(f"Looking for photos from {formatted_date}...")
43
+
44
+ # First task: search for photos and organize them, with typed output
45
+ result = await agent.run_task(
46
+ goal=(
47
+ f"Open the Photos/Gallery app. Find photos taken on {formatted_date}. "
48
+ f"Create a new album named '{formatted_date} Memories' and "
49
+ f"move those photos into it. Count how many photos were moved."
50
+ ),
51
+ output=PhotosResult,
52
+ name="organize_photos",
53
+ )
54
+
55
+ # Handle and display the result
56
+ if result:
57
+ print("\n=== Photo Organization Complete ===")
58
+ print(f"Found: {result.found_photos} photos from {result.date_range}")
59
+
60
+ if result.album_created:
61
+ print(f"Created album: '{result.album_name}'")
62
+ print(f"Moved {result.photos_moved} photos to the album")
63
+ else:
64
+ print("No album was created")
65
+ else:
66
+ print("Failed to organize photos")
67
+
68
+ except Exception as e:
69
+ print(f"Error: {e}")
70
+ finally:
71
+ # Always clean up resources
72
+ agent.clean()
73
+
74
+
75
+ if __name__ == "__main__":
76
+ asyncio.run(main())