minitap-mobile-use 2.0.1__py3-none-any.whl → 2.1.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.

Files changed (60) hide show
  1. minitap/mobile_use/agents/cortex/cortex.md +7 -5
  2. minitap/mobile_use/agents/cortex/cortex.py +4 -1
  3. minitap/mobile_use/agents/cortex/types.py +1 -3
  4. minitap/mobile_use/agents/executor/executor.md +4 -5
  5. minitap/mobile_use/agents/executor/tool_node.py +6 -6
  6. minitap/mobile_use/agents/outputter/outputter.py +1 -2
  7. minitap/mobile_use/agents/planner/planner.md +11 -2
  8. minitap/mobile_use/agents/planner/planner.py +4 -1
  9. minitap/mobile_use/agents/planner/types.py +3 -4
  10. minitap/mobile_use/agents/summarizer/summarizer.py +2 -1
  11. minitap/mobile_use/config.py +15 -15
  12. minitap/mobile_use/context.py +3 -4
  13. minitap/mobile_use/controllers/mobile_command_controller.py +32 -20
  14. minitap/mobile_use/controllers/platform_specific_commands_controller.py +3 -4
  15. minitap/mobile_use/graph/graph.py +1 -0
  16. minitap/mobile_use/graph/state.py +9 -9
  17. minitap/mobile_use/main.py +5 -6
  18. minitap/mobile_use/sdk/agent.py +24 -24
  19. minitap/mobile_use/sdk/builders/agent_config_builder.py +7 -8
  20. minitap/mobile_use/sdk/builders/task_request_builder.py +9 -9
  21. minitap/mobile_use/sdk/examples/smart_notification_assistant.py +1 -2
  22. minitap/mobile_use/sdk/types/agent.py +5 -5
  23. minitap/mobile_use/sdk/types/task.py +19 -18
  24. minitap/mobile_use/sdk/utils.py +1 -1
  25. minitap/mobile_use/servers/config.py +1 -2
  26. minitap/mobile_use/servers/device_hardware_bridge.py +3 -4
  27. minitap/mobile_use/servers/start_servers.py +4 -4
  28. minitap/mobile_use/servers/stop_servers.py +2 -3
  29. minitap/mobile_use/services/llm.py +3 -2
  30. minitap/mobile_use/tools/index.py +10 -4
  31. minitap/mobile_use/tools/mobile/back.py +1 -1
  32. minitap/mobile_use/tools/mobile/clear_text.py +277 -0
  33. minitap/mobile_use/tools/mobile/copy_text_from.py +1 -1
  34. minitap/mobile_use/tools/mobile/erase_one_char.py +56 -0
  35. minitap/mobile_use/tools/mobile/find_packages.py +1 -1
  36. minitap/mobile_use/tools/mobile/input_text.py +4 -80
  37. minitap/mobile_use/tools/mobile/launch_app.py +1 -1
  38. minitap/mobile_use/tools/mobile/long_press_on.py +2 -4
  39. minitap/mobile_use/tools/mobile/open_link.py +1 -1
  40. minitap/mobile_use/tools/mobile/paste_text.py +1 -1
  41. minitap/mobile_use/tools/mobile/press_key.py +1 -1
  42. minitap/mobile_use/tools/mobile/stop_app.py +2 -4
  43. minitap/mobile_use/tools/mobile/swipe.py +1 -1
  44. minitap/mobile_use/tools/mobile/take_screenshot.py +1 -1
  45. minitap/mobile_use/tools/mobile/tap.py +2 -4
  46. minitap/mobile_use/tools/mobile/wait_for_animation_to_end.py +2 -4
  47. minitap/mobile_use/tools/tool_wrapper.py +1 -1
  48. minitap/mobile_use/tools/utils.py +86 -0
  49. minitap/mobile_use/utils/cli_helpers.py +1 -2
  50. minitap/mobile_use/utils/cli_selection.py +5 -6
  51. minitap/mobile_use/utils/decorators.py +21 -20
  52. minitap/mobile_use/utils/logger.py +3 -4
  53. minitap/mobile_use/utils/media.py +1 -1
  54. minitap/mobile_use/utils/ui_hierarchy.py +13 -5
  55. {minitap_mobile_use-2.0.1.dist-info → minitap_mobile_use-2.1.0.dist-info}/METADATA +11 -1
  56. minitap_mobile_use-2.1.0.dist-info/RECORD +96 -0
  57. minitap/mobile_use/tools/mobile/erase_text.py +0 -122
  58. minitap_mobile_use-2.0.1.dist-info/RECORD +0 -94
  59. {minitap_mobile_use-2.0.1.dist-info → minitap_mobile_use-2.1.0.dist-info}/WHEEL +0 -0
  60. {minitap_mobile_use-2.0.1.dist-info → minitap_mobile_use-2.1.0.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@ import uuid
6
6
  from datetime import datetime
7
7
  from pathlib import Path
8
8
  from types import NoneType
9
- from typing import Optional, TypeVar, overload
9
+ from typing import TypeVar, overload
10
10
 
11
11
  from adbutils import AdbClient
12
12
  from langchain_core.messages import AIMessage
@@ -61,7 +61,7 @@ from minitap.mobile_use.utils.recorder import log_agent_thought
61
61
 
62
62
  logger = get_logger(__name__)
63
63
 
64
- TOutput = TypeVar("TOutput", bound=Optional[BaseModel])
64
+ TOutput = TypeVar("TOutput", bound=BaseModel | None)
65
65
 
66
66
 
67
67
  class Agent:
@@ -74,9 +74,9 @@ class Agent:
74
74
  _device_context: DeviceContext
75
75
  _screen_api_client: ScreenApiClient
76
76
  _hw_bridge_client: DeviceHardwareClient
77
- _adb_client: Optional[AdbClient]
77
+ _adb_client: AdbClient | None
78
78
 
79
- def __init__(self, config: Optional[AgentConfig] = None):
79
+ def __init__(self, config: AgentConfig | None = None):
80
80
  self._config = config or get_default_agent_config()
81
81
  self._tasks = []
82
82
  self._tmp_traces_dir = Path(tempfile.gettempdir()) / "mobile-use-traces"
@@ -153,9 +153,9 @@ class Agent:
153
153
  *,
154
154
  goal: str,
155
155
  output: type[TOutput],
156
- profile: Optional[str | AgentProfile] = None,
157
- name: Optional[str] = None,
158
- ) -> Optional[TOutput]: ...
156
+ profile: str | AgentProfile | None = None,
157
+ name: str | None = None,
158
+ ) -> TOutput | None: ...
159
159
 
160
160
  @overload
161
161
  async def run_task(
@@ -163,9 +163,9 @@ class Agent:
163
163
  *,
164
164
  goal: str,
165
165
  output: str,
166
- profile: Optional[str | AgentProfile] = None,
167
- name: Optional[str] = None,
168
- ) -> Optional[str | dict]: ...
166
+ profile: str | AgentProfile | None = None,
167
+ name: str | None = None,
168
+ ) -> str | dict | None: ...
169
169
 
170
170
  @overload
171
171
  async def run_task(
@@ -173,25 +173,25 @@ class Agent:
173
173
  *,
174
174
  goal: str,
175
175
  output=None,
176
- profile: Optional[str | AgentProfile] = None,
177
- name: Optional[str] = None,
178
- ) -> Optional[str]: ...
176
+ profile: str | AgentProfile | None = None,
177
+ name: str | None = None,
178
+ ) -> str | None: ...
179
179
 
180
180
  @overload
181
- async def run_task(self, *, request: TaskRequest[None]) -> Optional[str | dict]: ...
181
+ async def run_task(self, *, request: TaskRequest[None]) -> str | dict | None: ...
182
182
 
183
183
  @overload
184
- async def run_task(self, *, request: TaskRequest[TOutput]) -> Optional[TOutput]: ...
184
+ async def run_task(self, *, request: TaskRequest[TOutput]) -> TOutput | None: ...
185
185
 
186
186
  async def run_task(
187
187
  self,
188
188
  *,
189
- goal: Optional[str] = None,
190
- output: Optional[type[TOutput] | str] = None,
191
- profile: Optional[str | AgentProfile] = None,
192
- name: Optional[str] = None,
193
- request: Optional[TaskRequest[TOutput]] = None,
194
- ) -> Optional[str | dict | TOutput]:
189
+ goal: str | None = None,
190
+ output: type[TOutput] | str | None = None,
191
+ profile: str | AgentProfile | None = None,
192
+ name: str | None = None,
193
+ request: TaskRequest[TOutput] | None = None,
194
+ ) -> str | dict | TOutput | None:
195
195
  if request is not None:
196
196
  return await self._run_task(request)
197
197
  if goal is None:
@@ -208,7 +208,7 @@ class Agent:
208
208
  task_request.with_name(name=name)
209
209
  return await self._run_task(task_request.build())
210
210
 
211
- async def _run_task(self, request: TaskRequest[TOutput]) -> Optional[str | dict | TOutput]:
211
+ async def _run_task(self, request: TaskRequest[TOutput]) -> str | dict | TOutput | None:
212
212
  if not self._initialized:
213
213
  raise AgentNotInitializedError()
214
214
 
@@ -382,9 +382,9 @@ class Agent:
382
382
  task_name: str,
383
383
  ctx: MobileUseContext,
384
384
  request: TaskRequest[TOutput],
385
- output_config: Optional[OutputConfig],
385
+ output_config: OutputConfig | None,
386
386
  state: State,
387
- ) -> Optional[str | dict | TOutput]:
387
+ ) -> str | dict | TOutput | None:
388
388
  if output_config and output_config.needs_structured_format():
389
389
  logger.info(f"[{task_name}] Generating structured output...")
390
390
  try:
@@ -3,7 +3,6 @@ Builder for AgentConfig objects using a fluent interface.
3
3
  """
4
4
 
5
5
  import copy
6
- from typing import Dict, List, Optional
7
6
 
8
7
  from langchain_core.callbacks.base import Callbacks
9
8
 
@@ -39,11 +38,11 @@ class AgentConfigBuilder:
39
38
 
40
39
  def __init__(self):
41
40
  """Initialize an empty AgentConfigBuilder."""
42
- self._agent_profiles: Dict[str, AgentProfile] = {}
43
- self._task_request_defaults: Optional[TaskRequestCommon] = None
44
- self._default_profile: Optional[str | AgentProfile] = None
45
- self._device_id: Optional[str] = None
46
- self._device_platform: Optional[DevicePlatform] = None
41
+ self._agent_profiles: dict[str, AgentProfile] = {}
42
+ self._task_request_defaults: TaskRequestCommon | None = None
43
+ self._default_profile: str | AgentProfile | None = None
44
+ self._device_id: str | None = None
45
+ self._device_platform: DevicePlatform | None = None
47
46
  self._servers: ServerConfig = get_default_servers()
48
47
  self._graph_config_callbacks: Callbacks = None
49
48
 
@@ -58,7 +57,7 @@ class AgentConfigBuilder:
58
57
  profile.llm_config.validate_providers()
59
58
  return self
60
59
 
61
- def add_profiles(self, profiles: List[AgentProfile]) -> "AgentConfigBuilder":
60
+ def add_profiles(self, profiles: list[AgentProfile]) -> "AgentConfigBuilder":
62
61
  """
63
62
  Add multiple agent profiles to the mobile-use agent.
64
63
 
@@ -130,7 +129,7 @@ class AgentConfigBuilder:
130
129
  self._servers.screen_api_base_url = url
131
130
  return self
132
131
 
133
- def with_adb_server(self, host: str, port: Optional[int] = None) -> "AgentConfigBuilder":
132
+ def with_adb_server(self, host: str, port: int | None = None) -> "AgentConfigBuilder":
134
133
  """
135
134
  Set the ADB server host and port.
136
135
 
@@ -3,7 +3,7 @@ Builder for TaskRequest objects using a fluent interface.
3
3
  """
4
4
 
5
5
  from pathlib import Path
6
- from typing import Generic, Optional, Self, TypeVar, cast
6
+ from typing import Self, TypeVar, cast
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
@@ -12,7 +12,7 @@ from minitap.mobile_use.sdk.types.agent import AgentProfile
12
12
  from minitap.mobile_use.sdk.types.task import TaskRequest, TaskRequestCommon
13
13
 
14
14
 
15
- TIn = TypeVar("TIn", bound=Optional[BaseModel])
15
+ TIn = TypeVar("TIn", bound=BaseModel | None)
16
16
  TOut = TypeVar("TOut", bound=BaseModel)
17
17
 
18
18
 
@@ -25,8 +25,8 @@ class TaskRequestCommonBuilder(BaseModel):
25
25
  self._max_steps = RECURSION_LIMIT
26
26
  self._record_trace = False
27
27
  self._trace_path = Path("mobile-use-traces")
28
- self._llm_output_path: Optional[Path] = None
29
- self._thoughts_output_path: Optional[Path] = None
28
+ self._llm_output_path: Path | None = None
29
+ self._thoughts_output_path: Path | None = None
30
30
 
31
31
  def with_max_steps(self, max_steps: int) -> Self:
32
32
  """
@@ -38,7 +38,7 @@ class TaskRequestCommonBuilder(BaseModel):
38
38
  self._max_steps = max_steps
39
39
  return self
40
40
 
41
- def with_trace_recording(self, enabled: bool = True, path: Optional[str] = None) -> Self:
41
+ def with_trace_recording(self, enabled: bool = True, path: str | None = None) -> Self:
42
42
  """
43
43
  Configure trace recording for the task.
44
44
 
@@ -92,7 +92,7 @@ class TaskRequestCommonBuilder(BaseModel):
92
92
  )
93
93
 
94
94
 
95
- class TaskRequestBuilder(TaskRequestCommonBuilder, Generic[TIn]):
95
+ class TaskRequestBuilder[TIn](TaskRequestCommonBuilder):
96
96
  """
97
97
  Builder class providing a fluent interface for creating TaskRequest objects.
98
98
 
@@ -114,10 +114,10 @@ class TaskRequestBuilder(TaskRequestCommonBuilder, Generic[TIn]):
114
114
  """Initialize an empty TaskRequestBuilder."""
115
115
  super().__init__()
116
116
  self._goal = goal
117
- self._profile: Optional[str | AgentProfile] = None
118
- self._name: Optional[str] = None
117
+ self._profile: str | AgentProfile | None = None
118
+ self._name: str | None = None
119
119
  self._output_description = None
120
- self._output_format: Optional[type[TIn]] = None
120
+ self._output_format: type[TIn] | None = None
121
121
 
122
122
  @classmethod
123
123
  def from_common(cls, goal: str, common: TaskRequestCommon):
@@ -20,7 +20,6 @@ Run:
20
20
  import asyncio
21
21
  from datetime import datetime
22
22
  from enum import Enum
23
- from typing import List
24
23
 
25
24
  from pydantic import BaseModel, Field
26
25
  from minitap.mobile_use.config import LLM, LLMConfig, LLMConfigUtils, LLMWithFallback
@@ -52,7 +51,7 @@ class NotificationSummary(BaseModel):
52
51
 
53
52
  total_count: int = Field(..., description="Total number of notifications found")
54
53
  high_priority_count: int = Field(0, description="Count of high priority notifications")
55
- notifications: List[Notification] = Field(
54
+ notifications: list[Notification] = Field(
56
55
  default_factory=list, description="List of individual notifications"
57
56
  )
58
57
 
@@ -1,4 +1,4 @@
1
- from typing import Dict, Literal, Optional
1
+ from typing import Literal
2
2
  from urllib.parse import urlparse
3
3
 
4
4
  from langchain_core.callbacks.base import Callbacks
@@ -15,7 +15,7 @@ class ApiBaseUrl(BaseModel):
15
15
 
16
16
  scheme: Literal["http", "https"]
17
17
  host: str
18
- port: Optional[int] = None
18
+ port: int | None = None
19
19
 
20
20
  def __eq__(self, other):
21
21
  if not isinstance(other, ApiBaseUrl):
@@ -67,11 +67,11 @@ class AgentConfig(BaseModel):
67
67
  servers: Custom server configurations.
68
68
  """
69
69
 
70
- agent_profiles: Dict[str, AgentProfile]
70
+ agent_profiles: dict[str, AgentProfile]
71
71
  task_request_defaults: TaskRequestCommon
72
72
  default_profile: AgentProfile
73
- device_id: Optional[str] = None
74
- device_platform: Optional[DevicePlatform] = None
73
+ device_id: str | None = None
74
+ device_platform: DevicePlatform | None = None
75
75
  servers: ServerConfig
76
76
  graph_config_callbacks: Callbacks = None
77
77
 
@@ -5,7 +5,8 @@ Task-related type definitions for the Mobile-use SDK.
5
5
  from datetime import datetime
6
6
  from enum import Enum
7
7
  from pathlib import Path
8
- from typing import Any, Generic, Optional, Type, TypeVar, overload
8
+ from typing import Any, TypeVar, overload
9
+
9
10
  from pydantic import BaseModel, Field
10
11
 
11
12
  from minitap.mobile_use.config import LLMConfig, get_default_llm_config
@@ -36,8 +37,8 @@ class AgentProfile(BaseModel):
36
37
  self,
37
38
  *,
38
39
  name: str,
39
- llm_config: Optional[LLMConfig] = None,
40
- from_file: Optional[str] = None,
40
+ llm_config: LLMConfig | None = None,
41
+ from_file: str | None = None,
41
42
  **kwargs,
42
43
  ):
43
44
  kwargs["name"] = name
@@ -64,7 +65,7 @@ class TaskStatus(str, Enum):
64
65
 
65
66
 
66
67
  T = TypeVar("T", bound=BaseModel)
67
- TOutput = TypeVar("TOutput", bound=Optional[BaseModel])
68
+ TOutput = TypeVar("TOutput", bound=BaseModel | None)
68
69
 
69
70
 
70
71
  class TaskRequestCommon(BaseModel):
@@ -75,11 +76,11 @@ class TaskRequestCommon(BaseModel):
75
76
  max_steps: int = RECURSION_LIMIT
76
77
  record_trace: bool = False
77
78
  trace_path: Path = Path("mobile-use-traces")
78
- llm_output_path: Optional[Path] = None
79
- thoughts_output_path: Optional[Path] = None
79
+ llm_output_path: Path | None = None
80
+ thoughts_output_path: Path | None = None
80
81
 
81
82
 
82
- class TaskRequest(TaskRequestCommon, Generic[TOutput]):
83
+ class TaskRequest[TOutput](TaskRequestCommon):
83
84
  """
84
85
  Defines the format of a mobile automation task request.
85
86
 
@@ -98,10 +99,10 @@ class TaskRequest(TaskRequestCommon, Generic[TOutput]):
98
99
  """
99
100
 
100
101
  goal: str
101
- profile: Optional[str] = None
102
- task_name: Optional[str] = None
103
- output_description: Optional[str] = None
104
- output_format: Optional[type[TOutput]] = None
102
+ profile: str | None = None
103
+ task_name: str | None = None
104
+ output_description: str | None = None
105
+ output_format: type[TOutput] | None = None
105
106
 
106
107
 
107
108
  class TaskResult(BaseModel):
@@ -116,11 +117,11 @@ class TaskResult(BaseModel):
116
117
  """
117
118
 
118
119
  content: Any = None
119
- error: Optional[str] = None
120
+ error: str | None = None
120
121
  execution_time_seconds: float
121
122
  steps_taken: int
122
123
 
123
- def get_as_model(self, model_class: Type[T]) -> T:
124
+ def get_as_model(self, model_class: type[T]) -> T:
124
125
  """
125
126
  Parse the content into a Pydantic model instance.
126
127
 
@@ -158,14 +159,14 @@ class Task(BaseModel):
158
159
  status: TaskStatus
159
160
  request: TaskRequest
160
161
  created_at: datetime
161
- ended_at: Optional[datetime] = None
162
- result: Optional[TaskResult] = None
162
+ ended_at: datetime | None = None
163
+ result: TaskResult | None = None
163
164
 
164
165
  def finalize(
165
166
  self,
166
- content: Optional[Any] = None,
167
- state: Optional[dict] = None,
168
- error: Optional[str] = None,
167
+ content: Any | None = None,
168
+ state: dict | None = None,
169
+ error: str | None = None,
169
170
  cancelled: bool = False,
170
171
  ):
171
172
  self.status = TaskStatus.COMPLETED if error is None else TaskStatus.FAILED
@@ -16,7 +16,7 @@ def load_llm_config_override(path: Path) -> LLMConfig:
16
16
  override_config_dict = {}
17
17
  if os.path.exists(path):
18
18
  logger.info(f"Loading custom LLM config from {path.resolve()}...")
19
- with open(path, "r") as f:
19
+ with open(path) as f:
20
20
  override_config_dict = load_jsonc(f)
21
21
  else:
22
22
  logger.warning("Custom LLM config not found - using the default config")
@@ -1,4 +1,3 @@
1
- from typing import Optional
2
1
  from dotenv import load_dotenv
3
2
  from minitap.mobile_use.servers.device_hardware_bridge import DEVICE_HARDWARE_BRIDGE_PORT
4
3
  from minitap.mobile_use.utils.logger import get_logger
@@ -11,7 +10,7 @@ logger = get_logger(__name__)
11
10
  class ServerSettings(BaseSettings):
12
11
  DEVICE_HARDWARE_BRIDGE_BASE_URL: str = f"http://localhost:{DEVICE_HARDWARE_BRIDGE_PORT}"
13
12
  DEVICE_SCREEN_API_PORT: int = 9998
14
- ADB_HOST: Optional[str] = None
13
+ ADB_HOST: str | None = None
15
14
 
16
15
  model_config = {"env_file": ".env", "extra": "ignore"}
17
16
 
@@ -4,7 +4,6 @@ import subprocess
4
4
  import threading
5
5
  import time
6
6
  from enum import Enum
7
- from typing import Optional
8
7
 
9
8
  import requests
10
9
  from minitap.mobile_use.context import DevicePlatform
@@ -24,7 +23,7 @@ class BridgeStatus(Enum):
24
23
 
25
24
 
26
25
  class DeviceHardwareBridge:
27
- def __init__(self, device_id: str, platform: DevicePlatform, adb_host: Optional[str] = None):
26
+ def __init__(self, device_id: str, platform: DevicePlatform, adb_host: str | None = None):
28
27
  self.process = None
29
28
  self.status = BridgeStatus.STOPPED
30
29
  self.thread = None
@@ -32,7 +31,7 @@ class DeviceHardwareBridge:
32
31
  self.lock = threading.Lock()
33
32
  self.device_id: str = device_id
34
33
  self.platform: DevicePlatform = platform
35
- self.adb_host: Optional[str] = adb_host
34
+ self.adb_host: str | None = adb_host
36
35
 
37
36
  def _run_maestro_studio(self):
38
37
  try:
@@ -207,6 +206,6 @@ class DeviceHardwareBridge:
207
206
  with self.lock:
208
207
  return {"status": self.status.value, "output": self.output[-10:]}
209
208
 
210
- def get_device_id(self) -> Optional[str]:
209
+ def get_device_id(self) -> str | None:
211
210
  with self.lock:
212
211
  return self.device_id
@@ -4,7 +4,7 @@ import signal
4
4
  import sys
5
5
  import time
6
6
  from enum import Enum
7
- from typing import Annotated, Optional
7
+ from typing import Annotated
8
8
 
9
9
  import requests
10
10
  import typer
@@ -22,7 +22,7 @@ bridge_instance = None
22
22
  shutdown_requested = False
23
23
 
24
24
 
25
- def check_device_screen_api_health(base_url: Optional[str] = None, max_retries=30, delay=1):
25
+ def check_device_screen_api_health(base_url: str | None = None, max_retries=30, delay=1):
26
26
  base_url = base_url or f"http://localhost:{server_settings.DEVICE_SCREEN_API_PORT}"
27
27
  health_url = f"{base_url}/health"
28
28
 
@@ -49,7 +49,7 @@ def check_device_screen_api_health(base_url: Optional[str] = None, max_retries=3
49
49
  return False
50
50
 
51
51
 
52
- def _start_device_screen_api_process() -> Optional[multiprocessing.Process]:
52
+ def _start_device_screen_api_process() -> multiprocessing.Process | None:
53
53
  try:
54
54
  process = multiprocessing.Process(target=start_device_screen_api, daemon=True)
55
55
  process.start()
@@ -61,7 +61,7 @@ def _start_device_screen_api_process() -> Optional[multiprocessing.Process]:
61
61
 
62
62
  def start_device_hardware_bridge(
63
63
  device_id: str, platform: DevicePlatform
64
- ) -> Optional[DeviceHardwareBridge]:
64
+ ) -> DeviceHardwareBridge | None:
65
65
  logger.info("Starting Device Hardware Bridge...")
66
66
 
67
67
  try:
@@ -1,6 +1,5 @@
1
1
  import sys
2
2
  import time
3
- from typing import List
4
3
 
5
4
  import psutil
6
5
  import requests
@@ -12,7 +11,7 @@ from minitap.mobile_use.utils.logger import get_server_logger
12
11
  logger = get_server_logger()
13
12
 
14
13
 
15
- def find_processes_by_name(name: str) -> List[psutil.Process]:
14
+ def find_processes_by_name(name: str) -> list[psutil.Process]:
16
15
  """Find all processes with the given name."""
17
16
  processes = []
18
17
  for proc in psutil.process_iter(["pid", "name", "cmdline"]):
@@ -26,7 +25,7 @@ def find_processes_by_name(name: str) -> List[psutil.Process]:
26
25
  return processes
27
26
 
28
27
 
29
- def find_processes_by_port(port: int) -> List[psutil.Process]:
28
+ def find_processes_by_port(port: int) -> list[psutil.Process]:
30
29
  processes = []
31
30
  for proc in psutil.process_iter(["pid", "name"]):
32
31
  try:
@@ -1,6 +1,7 @@
1
1
  import logging
2
- from typing import Awaitable, Callable, Literal, TypeVar
3
- from typing_extensions import overload
2
+ from typing import Literal, TypeVar
3
+ from collections.abc import Awaitable, Callable
4
+ from typing import overload
4
5
 
5
6
  from langchain_google_genai import ChatGoogleGenerativeAI
6
7
  from langchain_openai import ChatOpenAI
@@ -1,16 +1,17 @@
1
1
  from langchain_core.tools import BaseTool
2
+
2
3
  from minitap.mobile_use.context import MobileUseContext
3
4
  from minitap.mobile_use.tools.mobile.back import back_wrapper
5
+ from minitap.mobile_use.tools.mobile.clear_text import clear_text_wrapper
4
6
  from minitap.mobile_use.tools.mobile.copy_text_from import copy_text_from_wrapper
5
- from minitap.mobile_use.tools.mobile.erase_text import erase_text_wrapper
7
+ from minitap.mobile_use.tools.mobile.erase_one_char import erase_one_char_wrapper
8
+ from minitap.mobile_use.tools.mobile.find_packages import find_packages_wrapper
6
9
  from minitap.mobile_use.tools.mobile.input_text import input_text_wrapper
7
10
  from minitap.mobile_use.tools.mobile.launch_app import launch_app_wrapper
8
- from minitap.mobile_use.tools.mobile.find_packages import find_packages_wrapper
9
11
  from minitap.mobile_use.tools.mobile.long_press_on import long_press_on_wrapper
10
12
  from minitap.mobile_use.tools.mobile.open_link import open_link_wrapper
11
13
  from minitap.mobile_use.tools.mobile.paste_text import paste_text_wrapper
12
14
  from minitap.mobile_use.tools.mobile.press_key import press_key_wrapper
13
-
14
15
  from minitap.mobile_use.tools.mobile.stop_app import stop_app_wrapper
15
16
  from minitap.mobile_use.tools.mobile.swipe import swipe_wrapper
16
17
  from minitap.mobile_use.tools.mobile.take_screenshot import take_screenshot_wrapper
@@ -29,11 +30,12 @@ EXECUTOR_WRAPPERS_TOOLS = [
29
30
  take_screenshot_wrapper,
30
31
  copy_text_from_wrapper,
31
32
  input_text_wrapper,
33
+ erase_one_char_wrapper,
32
34
  find_packages_wrapper,
33
35
  launch_app_wrapper,
34
36
  stop_app_wrapper,
35
37
  paste_text_wrapper,
36
- erase_text_wrapper,
38
+ clear_text_wrapper,
37
39
  press_key_wrapper,
38
40
  wait_for_animation_to_end_wrapper,
39
41
  ]
@@ -44,6 +46,10 @@ def get_tools_from_wrappers(ctx: MobileUseContext, wrappers: list[ToolWrapper])
44
46
  return [wrapper.tool_fn_getter(ctx) for wrapper in wrappers]
45
47
 
46
48
 
49
+ def format_tools_list(ctx: MobileUseContext, wrappers: list[ToolWrapper]) -> str:
50
+ return "\n".join([tool.name for tool in get_tools_from_wrappers(ctx, wrappers)])
51
+
52
+
47
53
  def get_tool_wrapper_from_name(name: str) -> ToolWrapper | None:
48
54
  """Get the tool wrapper from the name."""
49
55
  for wrapper in EXECUTOR_WRAPPERS_TOOLS:
@@ -5,7 +5,7 @@ from langgraph.types import Command
5
5
  from minitap.mobile_use.constants import EXECUTOR_MESSAGES_KEY
6
6
  from minitap.mobile_use.controllers.mobile_command_controller import back as back_controller
7
7
  from minitap.mobile_use.tools.tool_wrapper import ToolWrapper
8
- from typing_extensions import Annotated
8
+ from typing import Annotated
9
9
  from minitap.mobile_use.context import MobileUseContext
10
10
  from minitap.mobile_use.graph.state import State
11
11
  from langgraph.prebuilt import InjectedState