droidrun 0.3.10.dev4__py3-none-any.whl → 0.3.10.dev6__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.
- droidrun/agent/codeact/codeact_agent.py +18 -25
- droidrun/agent/droid/events.py +4 -1
- droidrun/agent/executor/executor_agent.py +24 -38
- droidrun/agent/executor/prompts.py +0 -108
- droidrun/agent/manager/manager_agent.py +104 -87
- droidrun/agent/utils/llm_picker.py +63 -1
- droidrun/agent/utils/tools.py +29 -0
- droidrun/app_cards/app_card_provider.py +27 -0
- droidrun/app_cards/providers/__init__.py +7 -0
- droidrun/app_cards/providers/composite_provider.py +97 -0
- droidrun/app_cards/providers/local_provider.py +116 -0
- droidrun/app_cards/providers/server_provider.py +126 -0
- droidrun/cli/main.py +241 -30
- droidrun/config_manager/__init__.py +0 -2
- droidrun/config_manager/config_manager.py +45 -101
- droidrun/config_manager/path_resolver.py +1 -1
- droidrun/config_manager/prompt_loader.py +48 -51
- droidrun/portal.py +17 -0
- droidrun/tools/adb.py +13 -34
- {droidrun-0.3.10.dev4.dist-info → droidrun-0.3.10.dev6.dist-info}/METADATA +3 -12
- {droidrun-0.3.10.dev4.dist-info → droidrun-0.3.10.dev6.dist-info}/RECORD +24 -20
- droidrun/config_manager/app_card_loader.py +0 -148
- {droidrun-0.3.10.dev4.dist-info → droidrun-0.3.10.dev6.dist-info}/WHEEL +0 -0
- {droidrun-0.3.10.dev4.dist-info → droidrun-0.3.10.dev6.dist-info}/entry_points.txt +0 -0
- {droidrun-0.3.10.dev4.dist-info → droidrun-0.3.10.dev6.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,3 @@
|
|
1
|
-
from droidrun.config_manager.app_card_loader import AppCardLoader
|
2
1
|
from droidrun.config_manager.config_manager import (
|
3
2
|
AgentConfig,
|
4
3
|
AppCardConfig,
|
@@ -23,5 +22,4 @@ __all__ = [
|
|
23
22
|
"TracingConfig",
|
24
23
|
"LoggingConfig",
|
25
24
|
"ToolsConfig",
|
26
|
-
"AppCardLoader",
|
27
25
|
]
|
@@ -35,30 +35,38 @@ agent:
|
|
35
35
|
# Enable vision capabilities (screenshots)
|
36
36
|
vision: false
|
37
37
|
# System prompt filename (located in prompts_dir/codeact/)
|
38
|
-
system_prompt: system.
|
38
|
+
system_prompt: system.jinja2
|
39
39
|
# User prompt filename (located in prompts_dir/codeact/)
|
40
|
-
user_prompt: user.
|
40
|
+
user_prompt: user.jinja2
|
41
41
|
|
42
42
|
# Manager Agent Configuration
|
43
43
|
manager:
|
44
44
|
# Enable vision capabilities (screenshots)
|
45
45
|
vision: false
|
46
46
|
# System prompt filename (located in prompts_dir/manager/)
|
47
|
-
system_prompt: system.
|
47
|
+
system_prompt: system.jinja2
|
48
48
|
|
49
49
|
# Executor Agent Configuration
|
50
50
|
executor:
|
51
51
|
# Enable vision capabilities (screenshots)
|
52
52
|
vision: false
|
53
53
|
# System prompt filename (located in prompts_dir/executor/)
|
54
|
-
system_prompt: system.
|
54
|
+
system_prompt: system.jinja2
|
55
55
|
|
56
56
|
# App Cards Configuration
|
57
57
|
app_cards:
|
58
58
|
# Enable app-specific instruction cards
|
59
59
|
enabled: true
|
60
|
-
#
|
60
|
+
# Mode: local (file-based), server (HTTP API), or composite (server with local fallback)
|
61
|
+
mode: local
|
62
|
+
# Directory containing app card files (for local/composite modes)
|
61
63
|
app_cards_dir: config/app_cards
|
64
|
+
# Server URL for remote app cards (for server/composite modes)
|
65
|
+
server_url: null
|
66
|
+
# Server request timeout in seconds
|
67
|
+
server_timeout: 2.0
|
68
|
+
# Number of server retry attempts
|
69
|
+
server_max_retries: 2
|
62
70
|
|
63
71
|
# === LLM Profiles ===
|
64
72
|
# Define LLM configurations for each agent type
|
@@ -170,29 +178,33 @@ class LLMProfile:
|
|
170
178
|
class CodeActConfig:
|
171
179
|
"""CodeAct agent configuration."""
|
172
180
|
vision: bool = False
|
173
|
-
system_prompt: str = "system.
|
174
|
-
user_prompt: str = "user.
|
181
|
+
system_prompt: str = "system.jinja2"
|
182
|
+
user_prompt: str = "user.jinja2"
|
175
183
|
|
176
184
|
|
177
185
|
@dataclass
|
178
186
|
class ManagerConfig:
|
179
187
|
"""Manager agent configuration."""
|
180
188
|
vision: bool = False
|
181
|
-
system_prompt: str = "system.
|
189
|
+
system_prompt: str = "system.jinja2"
|
182
190
|
|
183
191
|
|
184
192
|
@dataclass
|
185
193
|
class ExecutorConfig:
|
186
194
|
"""Executor agent configuration."""
|
187
195
|
vision: bool = False
|
188
|
-
system_prompt: str = "system.
|
196
|
+
system_prompt: str = "system.jinja2"
|
189
197
|
|
190
198
|
|
191
199
|
@dataclass
|
192
200
|
class AppCardConfig:
|
193
201
|
"""App card configuration."""
|
194
202
|
enabled: bool = True
|
203
|
+
mode: str = "local" # local | server | composite
|
195
204
|
app_cards_dir: str = "config/app_cards"
|
205
|
+
server_url: Optional[str] = None
|
206
|
+
server_timeout: float = 2.0
|
207
|
+
server_max_retries: int = 2
|
196
208
|
|
197
209
|
|
198
210
|
@dataclass
|
@@ -210,20 +222,24 @@ class AgentConfig:
|
|
210
222
|
app_cards: AppCardConfig = field(default_factory=AppCardConfig)
|
211
223
|
|
212
224
|
def get_codeact_system_prompt_path(self) -> str:
|
213
|
-
"""Get
|
214
|
-
|
225
|
+
"""Get resolved absolute path to CodeAct system prompt."""
|
226
|
+
path = f"{self.prompts_dir}/codeact/{self.codeact.system_prompt}"
|
227
|
+
return str(PathResolver.resolve(path, must_exist=True))
|
215
228
|
|
216
229
|
def get_codeact_user_prompt_path(self) -> str:
|
217
|
-
"""Get
|
218
|
-
|
230
|
+
"""Get resolved absolute path to CodeAct user prompt."""
|
231
|
+
path = f"{self.prompts_dir}/codeact/{self.codeact.user_prompt}"
|
232
|
+
return str(PathResolver.resolve(path, must_exist=True))
|
219
233
|
|
220
234
|
def get_manager_system_prompt_path(self) -> str:
|
221
|
-
"""Get
|
222
|
-
|
235
|
+
"""Get resolved absolute path to Manager system prompt."""
|
236
|
+
path = f"{self.prompts_dir}/manager/{self.manager.system_prompt}"
|
237
|
+
return str(PathResolver.resolve(path, must_exist=True))
|
223
238
|
|
224
239
|
def get_executor_system_prompt_path(self) -> str:
|
225
|
-
"""Get
|
226
|
-
|
240
|
+
"""Get resolved absolute path to Executor system prompt."""
|
241
|
+
path = f"{self.prompts_dir}/executor/{self.executor.system_prompt}"
|
242
|
+
return str(PathResolver.resolve(path, must_exist=True))
|
227
243
|
|
228
244
|
|
229
245
|
@dataclass
|
@@ -372,6 +388,7 @@ class ConfigManager:
|
|
372
388
|
|
373
389
|
Usage:
|
374
390
|
from droidrun.config_manager.config_manager import ConfigManager
|
391
|
+
from droidrun.agent.utils.llm_picker import load_llms_from_profiles
|
375
392
|
|
376
393
|
# Create config instance (singleton pattern)
|
377
394
|
config = ConfigManager()
|
@@ -379,12 +396,19 @@ class ConfigManager:
|
|
379
396
|
# Access typed config objects
|
380
397
|
print(config.agent.max_steps)
|
381
398
|
|
382
|
-
# Load all LLMs
|
383
|
-
llms = config.
|
399
|
+
# Load all LLMs from profiles
|
400
|
+
llms = load_llms_from_profiles(config.llm_profiles)
|
384
401
|
manager_llm = llms['manager']
|
385
402
|
executor_llm = llms['executor']
|
386
403
|
codeact_llm = llms['codeact']
|
387
404
|
|
405
|
+
# Load specific profiles with overrides
|
406
|
+
llms = load_llms_from_profiles(
|
407
|
+
config.llm_profiles,
|
408
|
+
profile_names=['manager', 'executor'],
|
409
|
+
manager={'temperature': 0.1}
|
410
|
+
)
|
411
|
+
|
388
412
|
# Modify and save
|
389
413
|
config.save()
|
390
414
|
"""
|
@@ -494,86 +518,6 @@ class ConfigManager:
|
|
494
518
|
|
495
519
|
return self._config.llm_profiles[profile_name]
|
496
520
|
|
497
|
-
def load_llm_from_profile(self, profile_name: str, **override_kwargs):
|
498
|
-
"""
|
499
|
-
Load an LLM using a profile configuration.
|
500
|
-
|
501
|
-
Args:
|
502
|
-
profile_name: Name of the profile to use (fast, mid, smart, custom)
|
503
|
-
**override_kwargs: Additional kwargs to override profile settings
|
504
|
-
|
505
|
-
Returns:
|
506
|
-
Initialized LLM instance
|
507
|
-
|
508
|
-
Example:
|
509
|
-
# Use specific profile
|
510
|
-
llm = config.load_llm_from_profile("smart")
|
511
|
-
|
512
|
-
# Override specific settings
|
513
|
-
llm = config.load_llm_from_profile("fast", temperature=0.5)
|
514
|
-
"""
|
515
|
-
from droidrun.agent.utils.llm_picker import load_llm
|
516
|
-
|
517
|
-
profile = self.get_llm_profile(profile_name)
|
518
|
-
|
519
|
-
# Get kwargs from profile
|
520
|
-
kwargs = profile.to_load_llm_kwargs()
|
521
|
-
|
522
|
-
# Override with any provided kwargs
|
523
|
-
kwargs.update(override_kwargs)
|
524
|
-
|
525
|
-
# Load the LLM
|
526
|
-
return load_llm(provider_name=profile.provider, **kwargs)
|
527
|
-
|
528
|
-
def load_all_llms(self, profile_names: Optional[list[str]] = None, **override_kwargs_per_profile):
|
529
|
-
"""
|
530
|
-
Load multiple LLMs from profiles for different use cases.
|
531
|
-
|
532
|
-
Args:
|
533
|
-
profile_names: List of profile names to load. If None, loads agent-specific profiles
|
534
|
-
**override_kwargs_per_profile: Dict of profile-specific overrides
|
535
|
-
Example: manager={'temperature': 0.1}, executor={'max_tokens': 8000}
|
536
|
-
|
537
|
-
Returns:
|
538
|
-
Dict mapping profile names to initialized LLM instances
|
539
|
-
|
540
|
-
Example:
|
541
|
-
# Load all agent-specific profiles
|
542
|
-
llms = config.load_all_llms()
|
543
|
-
manager_llm = llms['manager']
|
544
|
-
executor_llm = llms['executor']
|
545
|
-
codeact_llm = llms['codeact']
|
546
|
-
|
547
|
-
# Load specific profiles
|
548
|
-
llms = config.load_all_llms(['manager', 'executor'])
|
549
|
-
|
550
|
-
# Load with overrides
|
551
|
-
llms = config.load_all_llms(
|
552
|
-
manager={'temperature': 0.1},
|
553
|
-
executor={'max_tokens': 8000}
|
554
|
-
)
|
555
|
-
"""
|
556
|
-
from droidrun.agent.utils.llm_picker import load_llm
|
557
|
-
|
558
|
-
if profile_names is None:
|
559
|
-
profile_names = ["manager", "executor", "codeact", "text_manipulator", "app_opener"]
|
560
|
-
|
561
|
-
llms = {}
|
562
|
-
for profile_name in profile_names:
|
563
|
-
profile = self.get_llm_profile(profile_name)
|
564
|
-
|
565
|
-
# Get kwargs from profile
|
566
|
-
kwargs = profile.to_load_llm_kwargs()
|
567
|
-
|
568
|
-
# Apply profile-specific overrides if provided
|
569
|
-
if profile_name in override_kwargs_per_profile:
|
570
|
-
kwargs.update(override_kwargs_per_profile[profile_name])
|
571
|
-
|
572
|
-
# Load the LLM
|
573
|
-
llms[profile_name] = load_llm(provider_name=profile.provider, **kwargs)
|
574
|
-
|
575
|
-
return llms
|
576
|
-
|
577
521
|
# ---------------- I/O ----------------
|
578
522
|
def _ensure_file_exists(self) -> None:
|
579
523
|
parent = self.path.parent
|
@@ -653,7 +597,7 @@ class ConfigManager:
|
|
653
597
|
|
654
598
|
Example:
|
655
599
|
>>> config.list_available_prompts("manager")
|
656
|
-
['system.
|
600
|
+
['system.jinja2', 'experimental.jinja2', 'minimal.jinja2']
|
657
601
|
"""
|
658
602
|
agent_type = agent_type.lower()
|
659
603
|
if agent_type not in ["codeact", "manager", "executor"]:
|
@@ -667,7 +611,7 @@ class ConfigManager:
|
|
667
611
|
return []
|
668
612
|
|
669
613
|
# List all .md files in the directory
|
670
|
-
return sorted([f.name for f in prompts_dir.glob("*.
|
614
|
+
return sorted([f.name for f in prompts_dir.glob("*.jinja2")])
|
671
615
|
|
672
616
|
# useful for tests to reset singleton state
|
673
617
|
@classmethod
|
@@ -64,7 +64,7 @@ class PathResolver:
|
|
64
64
|
output_dir = PathResolver.resolve("trajectories", create_if_missing=True)
|
65
65
|
|
66
66
|
# Loading prompts (must exist, checks both locations)
|
67
|
-
prompt = PathResolver.resolve("config/prompts/system.
|
67
|
+
prompt = PathResolver.resolve("config/prompts/system.jinja2", must_exist=True)
|
68
68
|
|
69
69
|
# Absolute path (used as-is)
|
70
70
|
abs_path = PathResolver.resolve("/tmp/output")
|
@@ -1,75 +1,72 @@
|
|
1
1
|
"""
|
2
|
-
Prompt loading utility
|
3
|
-
|
4
|
-
|
5
|
-
-
|
6
|
-
- {
|
7
|
-
-
|
2
|
+
Prompt loading utility using Jinja2 templates.
|
3
|
+
|
4
|
+
Features:
|
5
|
+
- Loads from absolute file paths (resolved by AgentConfig + PathResolver)
|
6
|
+
- Conditional rendering: {% if variable %}...{% endif %}
|
7
|
+
- Loops with slicing: {% for item in items[-5:] %}...{% endfor %}
|
8
|
+
- Filters: {{ variable|default("fallback") }}
|
9
|
+
- Missing variables: silently ignored (renders as empty string)
|
10
|
+
- Extra variables: silently ignored
|
8
11
|
"""
|
9
12
|
|
10
|
-
import re
|
11
13
|
from pathlib import Path
|
12
14
|
from typing import Any, Dict
|
13
15
|
|
14
|
-
from
|
16
|
+
from jinja2 import Environment
|
15
17
|
|
16
18
|
|
17
19
|
class PromptLoader:
|
18
|
-
"""
|
20
|
+
"""Simple Jinja2 template renderer - loads from absolute file paths."""
|
21
|
+
|
22
|
+
_env = None # Cached Jinja2 environment
|
23
|
+
|
24
|
+
@classmethod
|
25
|
+
def _get_environment(cls) -> Environment:
|
26
|
+
"""Get or create cached Jinja2 environment."""
|
27
|
+
if cls._env is None:
|
28
|
+
cls._env = Environment(
|
29
|
+
trim_blocks=True, # Remove first newline after block
|
30
|
+
lstrip_blocks=True, # Strip leading whitespace before blocks
|
31
|
+
keep_trailing_newline=False,
|
32
|
+
)
|
33
|
+
|
34
|
+
return cls._env
|
19
35
|
|
20
36
|
@staticmethod
|
21
|
-
def load_prompt(
|
37
|
+
def load_prompt(file_path: str, variables: Dict[str, Any] = None) -> str:
|
22
38
|
"""
|
23
|
-
Load
|
24
|
-
|
25
|
-
Path resolution:
|
26
|
-
- Checks working directory first (for user overrides)
|
27
|
-
- Falls back to project directory (for default prompts)
|
28
|
-
- Example: "config/prompts/codeact/system.md"
|
39
|
+
Load and render Jinja2 template from absolute file path.
|
29
40
|
|
30
|
-
|
31
|
-
|
32
|
-
- {{variable}} → becomes literal {variable} in output
|
33
|
-
- Missing variables → left as {variable} (no error)
|
41
|
+
Path resolution is handled by AgentConfig + PathResolver.
|
42
|
+
This method just loads and renders.
|
34
43
|
|
35
44
|
Args:
|
36
|
-
|
37
|
-
variables: Dict of
|
45
|
+
file_path: ABSOLUTE path to template file (from AgentConfig methods)
|
46
|
+
variables: Dict of variables to pass to template
|
47
|
+
- Missing variables: silently ignored (render as empty string)
|
48
|
+
- Extra variables: silently ignored
|
38
49
|
|
39
50
|
Returns:
|
40
|
-
|
51
|
+
Rendered prompt string
|
41
52
|
|
42
53
|
Raises:
|
43
|
-
FileNotFoundError: If
|
44
|
-
"""
|
45
|
-
# Resolve path (checks working dir, then project dir)
|
46
|
-
prompt_path = PathResolver.resolve(path, must_exist=True)
|
47
|
-
|
48
|
-
prompt_text = prompt_path.read_text(encoding="utf-8")
|
49
|
-
|
50
|
-
if variables is None:
|
51
|
-
return prompt_text
|
54
|
+
FileNotFoundError: If template file doesn't exist
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
escaped_pattern = re.compile(r'\{\{([^}]+)\}\}')
|
56
|
-
placeholders = {}
|
57
|
-
counter = [0]
|
58
|
-
|
59
|
-
def escape_replacer(match):
|
60
|
-
placeholder = f"__ESCAPED_{counter[0]}__"
|
61
|
-
placeholders[placeholder] = "{" + match.group(1) + "}"
|
62
|
-
counter[0] += 1
|
63
|
-
return placeholder
|
56
|
+
"""
|
57
|
+
path = Path(file_path)
|
64
58
|
|
65
|
-
|
59
|
+
if not path.exists():
|
60
|
+
raise FileNotFoundError(f"Prompt file not found: {file_path}")
|
66
61
|
|
67
|
-
#
|
68
|
-
|
69
|
-
prompt_text = prompt_text.replace(f"{{{key}}}", str(value))
|
62
|
+
# Read template content
|
63
|
+
template_content = path.read_text(encoding="utf-8")
|
70
64
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
65
|
+
# Get cached environment and create template from string
|
66
|
+
env = PromptLoader._get_environment()
|
67
|
+
template = env.from_string(template_content)
|
74
68
|
|
75
|
-
|
69
|
+
# Render with variables (empty dict if None)
|
70
|
+
# Missing variables render as empty string (default Undefined behavior)
|
71
|
+
# Extra variables are silently ignored
|
72
|
+
return template.render(**(variables or {}))
|
droidrun/portal.py
CHANGED
@@ -190,6 +190,23 @@ def setup_keyboard(device: AdbDevice):
|
|
190
190
|
except Exception as e:
|
191
191
|
raise Exception("Error setting up keyboard") from e
|
192
192
|
|
193
|
+
def disable_keyboard(device: AdbDevice, target_ime: str = "com.droidrun.portal/.DroidrunKeyboardIME"):
|
194
|
+
"""
|
195
|
+
Disable a specific IME (keyboard) and optionally switch to another.
|
196
|
+
By default, disables the DroidRun keyboard.
|
197
|
+
|
198
|
+
Args:
|
199
|
+
target_ime: The IME package/activity to disable (default: DroidRun keyboard)
|
200
|
+
|
201
|
+
Returns:
|
202
|
+
bool: True if disabled successfully, False otherwise
|
203
|
+
"""
|
204
|
+
try:
|
205
|
+
device.shell(f"ime disable {target_ime}")
|
206
|
+
return True
|
207
|
+
except Exception as e:
|
208
|
+
raise Exception("Error disabling keyboard") from e
|
209
|
+
|
193
210
|
def test():
|
194
211
|
device = adb.device()
|
195
212
|
ping_portal(device, debug=False)
|
droidrun/tools/adb.py
CHANGED
@@ -71,7 +71,9 @@ class AdbTools(Tools):
|
|
71
71
|
self.app_opener_llm = app_opener_llm
|
72
72
|
self.text_manipulator_llm = text_manipulator_llm
|
73
73
|
|
74
|
-
|
74
|
+
# Set up keyboard
|
75
|
+
from droidrun.portal import setup_keyboard
|
76
|
+
setup_keyboard(self.device)
|
75
77
|
|
76
78
|
# Set up TCP forwarding if requested
|
77
79
|
if self.use_tcp:
|
@@ -150,24 +152,6 @@ class AdbTools(Tools):
|
|
150
152
|
logger.error(f"Failed to remove TCP port forwarding: {e}")
|
151
153
|
return False
|
152
154
|
|
153
|
-
def setup_keyboard(self) -> bool:
|
154
|
-
"""
|
155
|
-
Set up the DroidRun keyboard as the default input method.
|
156
|
-
Simple setup that just switches to DroidRun keyboard without saving/restoring.
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
bool: True if setup was successful, False otherwise
|
160
|
-
"""
|
161
|
-
try:
|
162
|
-
self.device.shell("ime enable com.droidrun.portal/.DroidrunKeyboardIME")
|
163
|
-
self.device.shell("ime set com.droidrun.portal/.DroidrunKeyboardIME")
|
164
|
-
logger.debug("DroidRun keyboard setup completed")
|
165
|
-
return True
|
166
|
-
|
167
|
-
except Exception as e:
|
168
|
-
logger.error(f"Failed to setup DroidRun keyboard: {e}")
|
169
|
-
return False
|
170
|
-
|
171
155
|
def __del__(self):
|
172
156
|
"""Cleanup when the object is destroyed."""
|
173
157
|
if hasattr(self, "tcp_forwarded") and self.tcp_forwarded:
|
@@ -312,12 +296,9 @@ class AdbTools(Tools):
|
|
312
296
|
# Extract coordinates using the helper function
|
313
297
|
x, y = self._extract_element_coordinates_by_index(index)
|
314
298
|
|
315
|
-
logger.debug(
|
316
|
-
f"Tapping element with index {index} at coordinates ({x}, {y})"
|
317
|
-
)
|
318
299
|
# Get the device and tap at the coordinates
|
319
300
|
self.device.click(x, y)
|
320
|
-
|
301
|
+
print(f"Tapped element with index {index} at coordinates ({x}, {y})")
|
321
302
|
|
322
303
|
# Emit coordinate action event for trajectory recording
|
323
304
|
if self._ctx:
|
@@ -350,8 +331,7 @@ class AdbTools(Tools):
|
|
350
331
|
)
|
351
332
|
self._ctx.write_event_to_stream(tap_event)
|
352
333
|
|
353
|
-
|
354
|
-
time.sleep(0.5)
|
334
|
+
|
355
335
|
|
356
336
|
# Create a descriptive response
|
357
337
|
def find_element_by_index(elements, target_index):
|
@@ -463,7 +443,7 @@ class AdbTools(Tools):
|
|
463
443
|
|
464
444
|
self.device.swipe(start_x, start_y, end_x, end_y, float(duration_ms / 1000))
|
465
445
|
time.sleep(duration_ms / 1000)
|
466
|
-
|
446
|
+
print(
|
467
447
|
f"Swiped from ({start_x}, {start_y}) to ({end_x}, {end_y}) in {duration_ms} milliseconds"
|
468
448
|
)
|
469
449
|
return True
|
@@ -546,7 +526,7 @@ class AdbTools(Tools):
|
|
546
526
|
timeout=10,
|
547
527
|
)
|
548
528
|
|
549
|
-
|
529
|
+
print(
|
550
530
|
f"Keyboard input TCP response: {response.status_code}, {response.text}"
|
551
531
|
)
|
552
532
|
|
@@ -575,7 +555,7 @@ class AdbTools(Tools):
|
|
575
555
|
|
576
556
|
# Execute the command and capture output for better error handling
|
577
557
|
result = self.device.shell(cmd)
|
578
|
-
|
558
|
+
print(f"Content provider result: {result}")
|
579
559
|
|
580
560
|
if self._ctx:
|
581
561
|
input_event = InputTextActionEvent(
|
@@ -585,7 +565,7 @@ class AdbTools(Tools):
|
|
585
565
|
)
|
586
566
|
self._ctx.write_event_to_stream(input_event)
|
587
567
|
|
588
|
-
|
568
|
+
print(
|
589
569
|
f"Text input completed (clear={clear}): {text[:50]}{'...' if len(text) > 50 else ''}"
|
590
570
|
)
|
591
571
|
return f"Text input completed (clear={clear}): {text[:50]}{'...' if len(text) > 50 else ''}"
|
@@ -604,7 +584,7 @@ class AdbTools(Tools):
|
|
604
584
|
This presses the Android back button.
|
605
585
|
"""
|
606
586
|
try:
|
607
|
-
|
587
|
+
print("Pressing key BACK")
|
608
588
|
self.device.keyevent(4)
|
609
589
|
|
610
590
|
if self._ctx:
|
@@ -652,9 +632,8 @@ class AdbTools(Tools):
|
|
652
632
|
)
|
653
633
|
self._ctx.write_event_to_stream(key_event)
|
654
634
|
|
655
|
-
logger.debug(f"Pressing key {key_name}")
|
656
635
|
self.device.keyevent(keycode)
|
657
|
-
|
636
|
+
print(f"Pressed key {key_name}")
|
658
637
|
return f"Pressed key {key_name}"
|
659
638
|
except ValueError as e:
|
660
639
|
return f"Error: {str(e)}"
|
@@ -670,7 +649,7 @@ class AdbTools(Tools):
|
|
670
649
|
"""
|
671
650
|
try:
|
672
651
|
|
673
|
-
|
652
|
+
print(f"Starting app {package} with activity {activity}")
|
674
653
|
if not activity:
|
675
654
|
dumpsys_output = self.device.shell(
|
676
655
|
f"cmd package resolve-activity --brief {package}"
|
@@ -689,7 +668,7 @@ class AdbTools(Tools):
|
|
689
668
|
print(f"Activity: {activity}")
|
690
669
|
|
691
670
|
self.device.app_start(package, activity)
|
692
|
-
|
671
|
+
print(f"App started: {package} with activity {activity}")
|
693
672
|
return f"App started: {package} with activity {activity}"
|
694
673
|
except Exception as e:
|
695
674
|
return f"Error: {str(e)}"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: droidrun
|
3
|
-
Version: 0.3.10.
|
3
|
+
Version: 0.3.10.dev6
|
4
4
|
Summary: A framework for controlling Android devices through LLM agents
|
5
5
|
Project-URL: Homepage, https://github.com/droidrun/droidrun
|
6
6
|
Project-URL: Bug Tracker, https://github.com/droidrun/droidrun/issues
|
@@ -13,8 +13,6 @@ Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Intended Audience :: Information Technology
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
18
16
|
Classifier: Programming Language :: Python :: 3.11
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
20
18
|
Classifier: Programming Language :: Python :: 3.13
|
@@ -27,10 +25,11 @@ Classifier: Topic :: Software Development :: Testing
|
|
27
25
|
Classifier: Topic :: Software Development :: Testing :: Acceptance
|
28
26
|
Classifier: Topic :: System :: Emulators
|
29
27
|
Classifier: Topic :: Utilities
|
30
|
-
Requires-Python: >=3.
|
28
|
+
Requires-Python: >=3.11
|
31
29
|
Requires-Dist: adbutils>=2.10.2
|
32
30
|
Requires-Dist: apkutils==2.0.0
|
33
31
|
Requires-Dist: arize-phoenix>=12.3.0
|
32
|
+
Requires-Dist: httpx>=0.27.0
|
34
33
|
Requires-Dist: llama-index==0.14.4
|
35
34
|
Requires-Dist: posthog>=6.7.6
|
36
35
|
Requires-Dist: pydantic>=2.11.10
|
@@ -38,14 +37,6 @@ Requires-Dist: rich>=14.1.0
|
|
38
37
|
Provides-Extra: anthropic
|
39
38
|
Requires-Dist: anthropic>=0.67.0; extra == 'anthropic'
|
40
39
|
Requires-Dist: llama-index-llms-anthropic<0.9.0,>=0.8.6; extra == 'anthropic'
|
41
|
-
Provides-Extra: backend
|
42
|
-
Requires-Dist: aiohttp>=3.9.0; extra == 'backend'
|
43
|
-
Requires-Dist: fastapi>=0.104.0; extra == 'backend'
|
44
|
-
Requires-Dist: pydantic-settings>=2.0.0; extra == 'backend'
|
45
|
-
Requires-Dist: python-dotenv>=1.0.0; extra == 'backend'
|
46
|
-
Requires-Dist: python-multipart>=0.0.6; extra == 'backend'
|
47
|
-
Requires-Dist: uvicorn[standard]>=0.24.0; extra == 'backend'
|
48
|
-
Requires-Dist: websockets>=12.0; extra == 'backend'
|
49
40
|
Provides-Extra: deepseek
|
50
41
|
Requires-Dist: llama-index-llms-deepseek>=0.2.1; extra == 'deepseek'
|
51
42
|
Provides-Extra: dev
|