droidrun 0.3.5__py3-none-any.whl → 0.3.7__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 +16 -3
- droidrun/agent/codeact/events.py +3 -0
- droidrun/agent/common/events.py +5 -1
- droidrun/agent/droid/droid_agent.py +137 -84
- droidrun/agent/planner/events.py +2 -0
- droidrun/agent/planner/planner_agent.py +15 -5
- droidrun/agent/usage.py +213 -0
- droidrun/agent/utils/executer.py +1 -1
- droidrun/agent/utils/llm_picker.py +91 -54
- droidrun/agent/utils/trajectory.py +256 -154
- droidrun/cli/logs.py +4 -1
- droidrun/cli/main.py +3 -1
- droidrun/portal.py +62 -17
- droidrun/telemetry/events.py +1 -1
- droidrun/tools/adb.py +97 -167
- droidrun/tools/tools.py +0 -1
- {droidrun-0.3.5.dist-info → droidrun-0.3.7.dist-info}/METADATA +23 -24
- {droidrun-0.3.5.dist-info → droidrun-0.3.7.dist-info}/RECORD +21 -20
- {droidrun-0.3.5.dist-info → droidrun-0.3.7.dist-info}/WHEEL +0 -0
- {droidrun-0.3.5.dist-info → droidrun-0.3.7.dist-info}/entry_points.txt +0 -0
- {droidrun-0.3.5.dist-info → droidrun-0.3.7.dist-info}/licenses/LICENSE +0 -0
droidrun/cli/main.py
CHANGED
@@ -285,7 +285,7 @@ def cli(
|
|
285
285
|
tracing: bool,
|
286
286
|
debug: bool,
|
287
287
|
use_tcp: bool,
|
288
|
-
save_trajectory: str,
|
288
|
+
save_trajectory: str = "none",
|
289
289
|
):
|
290
290
|
"""DroidRun - Control your Android device through LLM agents."""
|
291
291
|
pass
|
@@ -601,6 +601,7 @@ if __name__ == "__main__":
|
|
601
601
|
base_url = None
|
602
602
|
api_base = None
|
603
603
|
ios = False
|
604
|
+
save_trajectory = "action"
|
604
605
|
allow_drag = False
|
605
606
|
run_command(
|
606
607
|
command=command,
|
@@ -620,4 +621,5 @@ if __name__ == "__main__":
|
|
620
621
|
api_key=api_key,
|
621
622
|
allow_drag=allow_drag,
|
622
623
|
ios=ios,
|
624
|
+
save_trajectory=save_trajectory,
|
623
625
|
)
|
droidrun/portal.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
import requests
|
2
|
-
import tempfile
|
3
|
-
import os
|
4
1
|
import contextlib
|
5
|
-
|
6
|
-
|
2
|
+
import os
|
3
|
+
import tempfile
|
4
|
+
|
5
|
+
import requests
|
6
|
+
from adbutils import AdbDevice, adb
|
7
7
|
from rich.console import Console
|
8
8
|
|
9
|
+
from droidrun.tools import AdbTools
|
10
|
+
|
9
11
|
REPO = "droidrun/droidrun-portal"
|
10
12
|
ASSET_NAME = "droidrun-portal"
|
11
13
|
GITHUB_API_HOSTS = ["https://api.github.com", "https://ungh.cc"]
|
@@ -85,16 +87,18 @@ def download_portal_apk(debug: bool = False):
|
|
85
87
|
os.unlink(tmp.name)
|
86
88
|
|
87
89
|
|
88
|
-
def enable_portal_accessibility(
|
89
|
-
device
|
90
|
-
|
91
|
-
)
|
90
|
+
def enable_portal_accessibility(
|
91
|
+
device: AdbDevice, service_name: str = A11Y_SERVICE_NAME
|
92
|
+
):
|
93
|
+
device.shell(f"settings put secure enabled_accessibility_services {service_name}")
|
92
94
|
device.shell("settings put secure accessibility_enabled 1")
|
93
95
|
|
94
96
|
|
95
|
-
def check_portal_accessibility(
|
97
|
+
def check_portal_accessibility(
|
98
|
+
device: AdbDevice, service_name: str = A11Y_SERVICE_NAME, debug: bool = False
|
99
|
+
) -> bool:
|
96
100
|
a11y_services = device.shell("settings get secure enabled_accessibility_services")
|
97
|
-
if not
|
101
|
+
if service_name not in a11y_services:
|
98
102
|
if debug:
|
99
103
|
print(a11y_services)
|
100
104
|
return False
|
@@ -115,14 +119,14 @@ def ping_portal(device: AdbDevice, debug: bool = False):
|
|
115
119
|
try:
|
116
120
|
packages = device.list_packages()
|
117
121
|
except Exception as e:
|
118
|
-
raise Exception(
|
122
|
+
raise Exception("Failed to list packages") from e
|
119
123
|
|
120
|
-
if not
|
124
|
+
if PORTAL_PACKAGE_NAME not in packages:
|
121
125
|
if debug:
|
122
126
|
print(packages)
|
123
127
|
raise Exception("Portal is not installed on the device")
|
124
128
|
|
125
|
-
if not check_portal_accessibility(device, debug):
|
129
|
+
if not check_portal_accessibility(device, debug=debug):
|
126
130
|
device.shell("am start -a android.settings.ACCESSIBILITY_SETTINGS")
|
127
131
|
raise Exception(
|
128
132
|
"Droidrun Portal is not enabled as an accessibility service on the device"
|
@@ -132,18 +136,59 @@ def ping_portal(device: AdbDevice, debug: bool = False):
|
|
132
136
|
def ping_portal_content(device: AdbDevice, debug: bool = False):
|
133
137
|
try:
|
134
138
|
state = device.shell("content query --uri content://com.droidrun.portal/state")
|
135
|
-
if
|
139
|
+
if "Row: 0 result=" not in state:
|
136
140
|
raise Exception("Failed to get state from Droidrun Portal")
|
137
141
|
except Exception as e:
|
138
|
-
raise Exception(
|
142
|
+
raise Exception("Droidrun Portal is not reachable") from e
|
139
143
|
|
140
144
|
|
141
145
|
def ping_portal_tcp(device: AdbDevice, debug: bool = False):
|
142
146
|
try:
|
143
147
|
tools = AdbTools(serial=device.serial, use_tcp=True)
|
144
148
|
except Exception as e:
|
145
|
-
raise Exception(
|
149
|
+
raise Exception("Failed to setup TCP forwarding") from e
|
150
|
+
|
151
|
+
|
152
|
+
def set_overlay_offset(device: AdbDevice, offset: int):
|
153
|
+
"""
|
154
|
+
Set the overlay offset using the /overlay_offset portal content provider endpoint.
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
cmd = f'content insert --uri "content://com.droidrun.portal/overlay_offset" --bind offset:i:{offset}'
|
158
|
+
device.shell(cmd)
|
159
|
+
except Exception as e:
|
160
|
+
raise Exception("Error setting overlay offset") from e
|
161
|
+
|
162
|
+
def toggle_overlay(device: AdbDevice, visible: bool):
|
163
|
+
"""toggle the overlay visibility.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
device: Device to toggle the overlay on
|
167
|
+
visible: Whether to show the overlay
|
168
|
+
|
169
|
+
throws:
|
170
|
+
Exception: If the overlay toggle fails
|
171
|
+
"""
|
172
|
+
try:
|
173
|
+
device.shell(
|
174
|
+
f"am broadcast -a com.droidrun.portal.TOGGLE_OVERLAY --ez overlay_visible {'true' if visible else 'false'}"
|
175
|
+
)
|
176
|
+
except Exception as e:
|
177
|
+
raise Exception("Failed to toggle overlay") from e
|
178
|
+
|
179
|
+
def setup_keyboard(device: AdbDevice):
|
180
|
+
"""
|
181
|
+
Set up the DroidRun keyboard as the default input method.
|
182
|
+
Simple setup that just switches to DroidRun keyboard without saving/restoring.
|
146
183
|
|
184
|
+
throws:
|
185
|
+
Exception: If the keyboard setup fails
|
186
|
+
"""
|
187
|
+
try:
|
188
|
+
device.shell("ime enable com.droidrun.portal/.DroidrunKeyboardIME")
|
189
|
+
device.shell("ime set com.droidrun.portal/.DroidrunKeyboardIME")
|
190
|
+
except Exception as e:
|
191
|
+
raise Exception("Error setting up keyboard") from e
|
147
192
|
|
148
193
|
def test():
|
149
194
|
device = adb.device()
|
droidrun/telemetry/events.py
CHANGED
droidrun/tools/adb.py
CHANGED
@@ -61,10 +61,13 @@ class AdbTools(Tools):
|
|
61
61
|
# Trajectory saving level
|
62
62
|
self.save_trajectories = "none"
|
63
63
|
|
64
|
+
self.setup_keyboard()
|
65
|
+
|
64
66
|
# Set up TCP forwarding if requested
|
65
67
|
if self.use_tcp:
|
66
68
|
self.setup_tcp_forward()
|
67
69
|
|
70
|
+
|
68
71
|
def setup_tcp_forward(self) -> bool:
|
69
72
|
"""
|
70
73
|
Set up ADB TCP port forwarding for communication with the portal app.
|
@@ -130,6 +133,24 @@ class AdbTools(Tools):
|
|
130
133
|
logger.error(f"Failed to remove TCP port forwarding: {e}")
|
131
134
|
return False
|
132
135
|
|
136
|
+
def setup_keyboard(self) -> bool:
|
137
|
+
"""
|
138
|
+
Set up the DroidRun keyboard as the default input method.
|
139
|
+
Simple setup that just switches to DroidRun keyboard without saving/restoring.
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
bool: True if setup was successful, False otherwise
|
143
|
+
"""
|
144
|
+
try:
|
145
|
+
self.device.shell("ime enable com.droidrun.portal/.DroidrunKeyboardIME")
|
146
|
+
self.device.shell("ime set com.droidrun.portal/.DroidrunKeyboardIME")
|
147
|
+
logger.debug("DroidRun keyboard setup completed")
|
148
|
+
return True
|
149
|
+
|
150
|
+
except Exception as e:
|
151
|
+
logger.error(f"Failed to setup DroidRun keyboard: {e}")
|
152
|
+
return False
|
153
|
+
|
133
154
|
def __del__(self):
|
134
155
|
"""Cleanup when the object is destroyed."""
|
135
156
|
if hasattr(self, "tcp_forwarded") and self.tcp_forwarded:
|
@@ -444,61 +465,33 @@ class AdbTools(Tools):
|
|
444
465
|
Result message
|
445
466
|
"""
|
446
467
|
try:
|
447
|
-
logger.debug(f"Inputting text: {text}")
|
448
|
-
|
449
|
-
# if self.use_tcp and self.tcp_forwarded:
|
450
|
-
# # Use TCP communication
|
451
|
-
# encoded_text = base64.b64encode(text.encode()).decode()
|
452
|
-
|
453
|
-
# payload = {"base64_text": encoded_text}
|
454
|
-
# response = requests.post(
|
455
|
-
# f"{self.tcp_base_url}/keyboard/input",
|
456
|
-
# json=payload,
|
457
|
-
# headers={"Content-Type": "application/json"},
|
458
|
-
# timeout=10,
|
459
|
-
# )
|
460
|
-
|
461
|
-
# logger.debug(
|
462
|
-
# f"Keyboard input TCP response: {response.status_code}, {response.text}"
|
463
|
-
# )
|
464
|
-
|
465
|
-
# if response.status_code != 200:
|
466
|
-
# return f"Error: HTTP request failed with status {response.status_code}: {response.text}"
|
467
|
-
|
468
|
-
# else:
|
469
|
-
# Fallback to content provider method
|
470
|
-
# Save the current keyboard
|
471
|
-
original_ime = self.device.shell(
|
472
|
-
"settings get secure default_input_method"
|
473
|
-
)
|
474
|
-
original_ime = original_ime.strip()
|
475
|
-
|
476
|
-
# Enable the Droidrun keyboard
|
477
|
-
self.device.shell("ime enable com.droidrun.portal/.DroidrunKeyboardIME")
|
478
468
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
469
|
+
if self.use_tcp and self.tcp_forwarded:
|
470
|
+
# Use TCP communication
|
471
|
+
encoded_text = base64.b64encode(text.encode()).decode()
|
472
|
+
|
473
|
+
payload = {"base64_text": encoded_text}
|
474
|
+
response = requests.post(
|
475
|
+
f"{self.tcp_base_url}/keyboard/input",
|
476
|
+
json=payload,
|
477
|
+
headers={"Content-Type": "application/json"},
|
478
|
+
timeout=10,
|
479
|
+
)
|
487
480
|
|
488
|
-
|
489
|
-
|
481
|
+
logger.debug(
|
482
|
+
f"Keyboard input TCP response: {response.status_code}, {response.text}"
|
483
|
+
)
|
490
484
|
|
491
|
-
|
492
|
-
|
485
|
+
if response.status_code != 200:
|
486
|
+
return f"Error: HTTP request failed with status {response.status_code}: {response.text}"
|
493
487
|
|
494
|
-
|
495
|
-
|
496
|
-
|
488
|
+
else:
|
489
|
+
# Fallback to content provider method
|
490
|
+
# Encode the text to Base64
|
491
|
+
encoded_text = base64.b64encode(text.encode()).decode()
|
497
492
|
|
498
|
-
|
499
|
-
|
500
|
-
)
|
501
|
-
return f"Text input completed: {text[:50]}{'...' if len(text) > 50 else ''}"
|
493
|
+
cmd = f'content insert --uri "content://com.droidrun.portal/keyboard/input" --bind base64_text:s:"{encoded_text}"'
|
494
|
+
self.device.shell(cmd)
|
502
495
|
|
503
496
|
if self._ctx:
|
504
497
|
input_event = InputTextActionEvent(
|
@@ -528,13 +521,13 @@ class AdbTools(Tools):
|
|
528
521
|
"""
|
529
522
|
try:
|
530
523
|
logger.debug("Pressing key BACK")
|
531
|
-
self.device.keyevent(
|
524
|
+
self.device.keyevent(4)
|
532
525
|
|
533
526
|
if self._ctx:
|
534
527
|
key_event = KeyPressActionEvent(
|
535
528
|
action_type="key_press",
|
536
529
|
description=f"Pressed key BACK",
|
537
|
-
keycode=
|
530
|
+
keycode=4,
|
538
531
|
key_name="BACK",
|
539
532
|
)
|
540
533
|
self._ctx.write_event_to_stream(key_event)
|
@@ -647,22 +640,50 @@ class AdbTools(Tools):
|
|
647
640
|
except ValueError as e:
|
648
641
|
return f"Error: {str(e)}"
|
649
642
|
|
650
|
-
def take_screenshot(self) -> Tuple[str, bytes]:
|
643
|
+
def take_screenshot(self, hide_overlay: bool = True) -> Tuple[str, bytes]:
|
651
644
|
"""
|
652
645
|
Take a screenshot of the device.
|
653
646
|
This function captures the current screen and adds the screenshot to context in the next message.
|
654
647
|
Also stores the screenshot in the screenshots list with timestamp for later GIF creation.
|
648
|
+
|
649
|
+
Args:
|
650
|
+
hide_overlay: Whether to hide the overlay elements during screenshot (default: True)
|
655
651
|
"""
|
656
652
|
try:
|
657
653
|
logger.debug("Taking screenshot")
|
658
|
-
|
659
|
-
# Fallback to ADB screenshot method
|
660
|
-
img = self.device.screenshot()
|
661
|
-
img_buf = io.BytesIO()
|
662
654
|
img_format = "PNG"
|
663
|
-
|
664
|
-
|
665
|
-
|
655
|
+
image_bytes = None
|
656
|
+
|
657
|
+
if self.use_tcp and self.tcp_forwarded:
|
658
|
+
# Add hideOverlay parameter to URL
|
659
|
+
url = f"{self.tcp_base_url}/screenshot"
|
660
|
+
if not hide_overlay:
|
661
|
+
url += "?hideOverlay=false"
|
662
|
+
|
663
|
+
response = requests.get(url, timeout=10)
|
664
|
+
if response.status_code == 200:
|
665
|
+
tcp_response = response.json()
|
666
|
+
|
667
|
+
# Check if response has the expected format with data field
|
668
|
+
if tcp_response.get("status") == "success" and "data" in tcp_response:
|
669
|
+
# Decode base64 string to bytes
|
670
|
+
base64_data = tcp_response["data"]
|
671
|
+
image_bytes = base64.b64decode(base64_data)
|
672
|
+
logger.debug("Screenshot taken via TCP")
|
673
|
+
else:
|
674
|
+
# Handle error response from server
|
675
|
+
error_msg = tcp_response.get("error", "Unknown error")
|
676
|
+
raise ValueError(f"Error taking screenshot via TCP: {error_msg}")
|
677
|
+
else:
|
678
|
+
raise ValueError(f"Error taking screenshot via TCP: {response.status_code}")
|
679
|
+
|
680
|
+
else:
|
681
|
+
# Fallback to ADB screenshot method
|
682
|
+
img = self.device.screenshot()
|
683
|
+
img_buf = io.BytesIO()
|
684
|
+
img.save(img_buf, format=img_format)
|
685
|
+
image_bytes = img_buf.getvalue()
|
686
|
+
logger.debug("Screenshot taken via ADB")
|
666
687
|
|
667
688
|
# Store screenshot with timestamp
|
668
689
|
self.screenshots.append(
|
@@ -806,14 +827,23 @@ class AdbTools(Tools):
|
|
806
827
|
"message": "Failed to parse state data from ContentProvider response",
|
807
828
|
}
|
808
829
|
|
809
|
-
if isinstance(state_data, dict)
|
810
|
-
data_str =
|
811
|
-
|
812
|
-
|
813
|
-
|
830
|
+
if isinstance(state_data, dict):
|
831
|
+
data_str = None
|
832
|
+
if "data" in state_data:
|
833
|
+
data_str = state_data["data"]
|
834
|
+
|
835
|
+
if data_str:
|
836
|
+
try:
|
837
|
+
combined_data = json.loads(data_str)
|
838
|
+
except json.JSONDecodeError:
|
839
|
+
return {
|
840
|
+
"error": "Parse Error",
|
841
|
+
"message": "Failed to parse JSON data from ContentProvider response",
|
842
|
+
}
|
843
|
+
else:
|
814
844
|
return {
|
815
|
-
"error": "
|
816
|
-
"message": "
|
845
|
+
"error": "Format Error",
|
846
|
+
"message": "Neither 'data' nor 'message' field found in ContentProvider response",
|
817
847
|
}
|
818
848
|
else:
|
819
849
|
return {
|
@@ -868,106 +898,6 @@ class AdbTools(Tools):
|
|
868
898
|
"message": f"Error getting combined state: {str(e)}",
|
869
899
|
}
|
870
900
|
|
871
|
-
def get_a11y_tree(self) -> Dict[str, Any]:
|
872
|
-
"""
|
873
|
-
Get just the accessibility tree using the /a11y_tree endpoint.
|
874
|
-
|
875
|
-
Returns:
|
876
|
-
Dictionary containing accessibility tree data
|
877
|
-
"""
|
878
|
-
try:
|
879
|
-
if self.use_tcp and self.tcp_forwarded:
|
880
|
-
response = requests.get(f"{self.tcp_base_url}/a11y_tree", timeout=10)
|
881
|
-
|
882
|
-
if response.status_code == 200:
|
883
|
-
tcp_response = response.json()
|
884
|
-
|
885
|
-
# Check if response has the expected format with data field
|
886
|
-
if isinstance(tcp_response, dict) and "data" in tcp_response:
|
887
|
-
data_str = tcp_response["data"]
|
888
|
-
try:
|
889
|
-
return json.loads(data_str)
|
890
|
-
except json.JSONDecodeError:
|
891
|
-
return {
|
892
|
-
"error": "Parse Error",
|
893
|
-
"message": "Failed to parse JSON data from TCP response data field",
|
894
|
-
}
|
895
|
-
else:
|
896
|
-
# Fallback: assume direct JSON format
|
897
|
-
return tcp_response
|
898
|
-
else:
|
899
|
-
return {
|
900
|
-
"error": "HTTP Error",
|
901
|
-
"message": f"HTTP request failed with status {response.status_code}",
|
902
|
-
}
|
903
|
-
else:
|
904
|
-
# Fallback: use get_state and extract a11y_tree
|
905
|
-
state = self.get_state()
|
906
|
-
if "error" in state:
|
907
|
-
return state
|
908
|
-
return {"a11y_tree": state.get("a11y_tree", [])}
|
909
|
-
|
910
|
-
except requests.exceptions.RequestException as e:
|
911
|
-
return {
|
912
|
-
"error": "TCP Error",
|
913
|
-
"message": f"TCP request failed: {str(e)}",
|
914
|
-
}
|
915
|
-
except Exception as e:
|
916
|
-
return {
|
917
|
-
"error": str(e),
|
918
|
-
"message": f"Error getting a11y tree: {str(e)}",
|
919
|
-
}
|
920
|
-
|
921
|
-
def get_phone_state(self) -> Dict[str, Any]:
|
922
|
-
"""
|
923
|
-
Get just the phone state using the /phone_state endpoint.
|
924
|
-
|
925
|
-
Returns:
|
926
|
-
Dictionary containing phone state data
|
927
|
-
"""
|
928
|
-
try:
|
929
|
-
if self.use_tcp and self.tcp_forwarded:
|
930
|
-
response = requests.get(f"{self.tcp_base_url}/phone_state", timeout=10)
|
931
|
-
|
932
|
-
if response.status_code == 200:
|
933
|
-
tcp_response = response.json()
|
934
|
-
|
935
|
-
# Check if response has the expected format with data field
|
936
|
-
if isinstance(tcp_response, dict) and "data" in tcp_response:
|
937
|
-
data_str = tcp_response["data"]
|
938
|
-
try:
|
939
|
-
return json.loads(data_str)
|
940
|
-
except json.JSONDecodeError:
|
941
|
-
return {
|
942
|
-
"error": "Parse Error",
|
943
|
-
"message": "Failed to parse JSON data from TCP response data field",
|
944
|
-
}
|
945
|
-
else:
|
946
|
-
# Fallback: assume direct JSON format
|
947
|
-
return tcp_response
|
948
|
-
else:
|
949
|
-
return {
|
950
|
-
"error": "HTTP Error",
|
951
|
-
"message": f"HTTP request failed with status {response.status_code}",
|
952
|
-
}
|
953
|
-
else:
|
954
|
-
# Fallback: use get_state and extract phone_state
|
955
|
-
state = self.get_state()
|
956
|
-
if "error" in state:
|
957
|
-
return state
|
958
|
-
return {"phone_state": state.get("phone_state", {})}
|
959
|
-
|
960
|
-
except requests.exceptions.RequestException as e:
|
961
|
-
return {
|
962
|
-
"error": "TCP Error",
|
963
|
-
"message": f"TCP request failed: {str(e)}",
|
964
|
-
}
|
965
|
-
except Exception as e:
|
966
|
-
return {
|
967
|
-
"error": str(e),
|
968
|
-
"message": f"Error getting phone state: {str(e)}",
|
969
|
-
}
|
970
|
-
|
971
901
|
def ping(self) -> Dict[str, Any]:
|
972
902
|
"""
|
973
903
|
Test the TCP connection using the /ping endpoint.
|
droidrun/tools/tools.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: droidrun
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.7
|
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
|
@@ -26,32 +26,31 @@ Classifier: Topic :: Software Development :: Testing :: Acceptance
|
|
26
26
|
Classifier: Topic :: System :: Emulators
|
27
27
|
Classifier: Topic :: Utilities
|
28
28
|
Requires-Python: >=3.11
|
29
|
-
Requires-Dist: adbutils
|
30
|
-
Requires-Dist: aiofiles>=23.0.0
|
31
|
-
Requires-Dist: anthropic>=0.7.0
|
29
|
+
Requires-Dist: adbutils>=2.10.2
|
32
30
|
Requires-Dist: apkutils==2.0.0
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist:
|
35
|
-
Requires-Dist:
|
36
|
-
Requires-Dist:
|
37
|
-
|
38
|
-
Requires-Dist:
|
39
|
-
Requires-Dist: llama-index-llms-
|
40
|
-
|
41
|
-
Requires-Dist: llama-index-llms-
|
42
|
-
Requires-Dist: llama-index-llms-openai-like
|
43
|
-
Requires-Dist: openai>=1.0.0
|
44
|
-
Requires-Dist: pillow>=10.0.0
|
45
|
-
Requires-Dist: posthog==6.0.2
|
46
|
-
Requires-Dist: pydantic>=2.0.0
|
47
|
-
Requires-Dist: python-dotenv>=1.0.0
|
48
|
-
Requires-Dist: rich>=13.0.0
|
31
|
+
Requires-Dist: llama-index>=0.13.6
|
32
|
+
Requires-Dist: posthog>=6.7.4
|
33
|
+
Requires-Dist: pydantic>=2.11.7
|
34
|
+
Requires-Dist: rich>=14.1.0
|
35
|
+
Provides-Extra: anthropic
|
36
|
+
Requires-Dist: anthropic>=0.67.0; extra == 'anthropic'
|
37
|
+
Requires-Dist: llama-index-llms-anthropic>=0.8.6; extra == 'anthropic'
|
38
|
+
Provides-Extra: deepseek
|
39
|
+
Requires-Dist: llama-index-llms-deepseek>=0.2.1; extra == 'deepseek'
|
49
40
|
Provides-Extra: dev
|
50
|
-
Requires-Dist: bandit>=1.
|
41
|
+
Requires-Dist: bandit>=1.8.6; extra == 'dev'
|
51
42
|
Requires-Dist: black>=23.0.0; extra == 'dev'
|
52
43
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
53
|
-
Requires-Dist: ruff>=0.
|
54
|
-
Requires-Dist: safety>=2.
|
44
|
+
Requires-Dist: ruff>=0.13.0; extra == 'dev'
|
45
|
+
Requires-Dist: safety>=3.2.11; extra == 'dev'
|
46
|
+
Provides-Extra: google
|
47
|
+
Requires-Dist: llama-index-llms-google-genai>=0.3.1; extra == 'google'
|
48
|
+
Provides-Extra: ollama
|
49
|
+
Requires-Dist: llama-index-llms-ollama>=0.7.2; extra == 'ollama'
|
50
|
+
Provides-Extra: openai
|
51
|
+
Requires-Dist: llama-index-llms-openai-like>=0.5.1; extra == 'openai'
|
52
|
+
Requires-Dist: llama-index-llms-openai>=0.5.6; extra == 'openai'
|
53
|
+
Requires-Dist: openai>=1.107.1; extra == 'openai'
|
55
54
|
Description-Content-Type: text/markdown
|
56
55
|
|
57
56
|
<picture>
|
@@ -89,7 +88,7 @@ DroidRun is a powerful framework for controlling Android and iOS devices through
|
|
89
88
|
## 📦 Installation
|
90
89
|
|
91
90
|
```bash
|
92
|
-
pip install droidrun
|
91
|
+
pip install droidrun[google,anthropic,openai,deepseek,ollama,dev]
|
93
92
|
```
|
94
93
|
|
95
94
|
## 🚀 Quickstart
|
@@ -1,13 +1,14 @@
|
|
1
1
|
droidrun/__init__.py,sha256=Cqt4NXZ-753220dlRZXglMkFT2ygcXSErMmqupGsi9A,622
|
2
2
|
droidrun/__main__.py,sha256=78o1Wr_Z-NrZy9yLWmEfNfRRhAiJGBr4Xi3lmbgkx3w,105
|
3
|
-
droidrun/portal.py,sha256=
|
3
|
+
droidrun/portal.py,sha256=4-1lNc28yxDSkg2qf_t36gXzTSflcJqfsxggYpUwmio,6169
|
4
4
|
droidrun/agent/__init__.py,sha256=4SqTJeGDvr_wT8rtN9J8hnN6P-pae663mkYr-JmzH4w,208
|
5
|
+
droidrun/agent/usage.py,sha256=ZTgqxRAgugJ9tk6ApZ2BJ5nB88pt9rhLyTFS5AnVUHo,7153
|
5
6
|
droidrun/agent/codeact/__init__.py,sha256=ZLDGT_lTTzyNm7pcBzdyRIGHJ2ZgbInJdhXZRbJLhSQ,278
|
6
|
-
droidrun/agent/codeact/codeact_agent.py,sha256=
|
7
|
-
droidrun/agent/codeact/events.py,sha256=
|
7
|
+
droidrun/agent/codeact/codeact_agent.py,sha256=34lGKarbXcPRJVghRPlpJAt9ZDKBG2iwZmWYs-wz8DQ,17123
|
8
|
+
droidrun/agent/codeact/events.py,sha256=R3lNWSRTHQBkIBlel6CzyEUuozD40_9cugTvQ6PZsvw,720
|
8
9
|
droidrun/agent/codeact/prompts.py,sha256=28HflWMNkC1ky0hGCzAxhJftjU2IIU1ZRUfya3S7M6I,1006
|
9
10
|
droidrun/agent/common/default.py,sha256=P07el-PrHsoqQMYsYxSSln6mFl-QY75vzwp1Dll_XmY,259
|
10
|
-
droidrun/agent/common/events.py,sha256=
|
11
|
+
droidrun/agent/common/events.py,sha256=xh1Qly2_ZC2edRr3Ad18QMe90ndRDxrJYZBWB3rqIjg,1183
|
11
12
|
droidrun/agent/context/__init__.py,sha256=upSJilVQUwQYWJuGr_8QrmJaReV3SNAc_Z61wQZzbK4,697
|
12
13
|
droidrun/agent/context/agent_persona.py,sha256=Mxd4HTyirWD-aqNlka1hQBdS-0I-lXJr2AjPMwDMUo8,390
|
13
14
|
droidrun/agent/context/context_injection_manager.py,sha256=sA33q2KPtX_4Yap8wM11T6ewlZC_0FIbKPEc400SHrE,2188
|
@@ -20,35 +21,35 @@ droidrun/agent/context/personas/big_agent.py,sha256=Gl_y4ykz3apGc203-KG2UbSOwf7g
|
|
20
21
|
droidrun/agent/context/personas/default.py,sha256=Xm07YCWoKjvlHAbQRtzE3vn7BVcz6wYcSVeg4FiojJQ,5060
|
21
22
|
droidrun/agent/context/personas/ui_expert.py,sha256=j0OKfN1jQSrREHcVeomMTDPCWLsZVX4aeuWN4Y-x3z0,4739
|
22
23
|
droidrun/agent/droid/__init__.py,sha256=3BfUVZiUQ8ATAJ_JmqQZQx53WoERRpQ4AyHW5WOgbRI,297
|
23
|
-
droidrun/agent/droid/droid_agent.py,sha256=
|
24
|
+
droidrun/agent/droid/droid_agent.py,sha256=Bd273hbefa7s1M-HW_FQEuhQWUKctDjhdlGyH6k455A,17650
|
24
25
|
droidrun/agent/droid/events.py,sha256=hjYpWcSffqP83rNv_GyOEc3CNSrdvlVPdUkaRU6QDJc,722
|
25
26
|
droidrun/agent/oneflows/reflector.py,sha256=I_tE0PBjvwWbS6SA8Qd41etxJglFgn8oScuKUxc9LEE,11621
|
26
27
|
droidrun/agent/planner/__init__.py,sha256=Fu0Ewtd-dIRLgHIL1DB_9EEKvQS_f1vjB8jgO5TbJXg,364
|
27
|
-
droidrun/agent/planner/events.py,sha256=
|
28
|
-
droidrun/agent/planner/planner_agent.py,sha256=
|
28
|
+
droidrun/agent/planner/events.py,sha256=AJEC5lqMc5qLoj8V7lIN08KXT0YEg3i1anjLqiNS6is,475
|
29
|
+
droidrun/agent/planner/planner_agent.py,sha256=3Jx6NZJ3IFmb5zi2gKxmPZ2nuLjKyUOVec3SdNAuhVY,11456
|
29
30
|
droidrun/agent/planner/prompts.py,sha256=Ci7Oeu3J4TAhx-tKGPZ9l6Wb3a81FSqC8cWW4jW73HI,6046
|
30
31
|
droidrun/agent/utils/__init__.py,sha256=JK6ygRjw7gzcQSG0HBEYLoVGH54QQAxJJ7HpIS5mgyc,44
|
31
32
|
droidrun/agent/utils/async_utils.py,sha256=IQBcWPwevm89B7R_UdMXk0unWeNCBA232b5kQGqoxNI,336
|
32
33
|
droidrun/agent/utils/chat_utils.py,sha256=KzmiNasXb9d0qH12ylRsWooKV4_9LXy_QifaD_xf_O0,12726
|
33
|
-
droidrun/agent/utils/executer.py,sha256
|
34
|
-
droidrun/agent/utils/llm_picker.py,sha256=
|
35
|
-
droidrun/agent/utils/trajectory.py,sha256=
|
34
|
+
droidrun/agent/utils/executer.py,sha256=EBDGBMUAL80fJ6yyyt0v4XElhcjEGwi4_K4oU3_Loxo,5203
|
35
|
+
droidrun/agent/utils/llm_picker.py,sha256=V35nrOeSUeXoIV0eDKHBo3ESTAKj-N4NRX75i0LO1JM,7230
|
36
|
+
droidrun/agent/utils/trajectory.py,sha256=xvfj274AdRdIAVz72889ndoj-dD0_iOmLQBVQbXNcP4,19510
|
36
37
|
droidrun/cli/__init__.py,sha256=DuwSRtZ8WILPd-nf-fZ7BaBsRgtofoInOF3JtJ9wag0,167
|
37
|
-
droidrun/cli/logs.py,sha256
|
38
|
-
droidrun/cli/main.py,sha256=
|
38
|
+
droidrun/cli/logs.py,sha256=-idRi3HTp7OfZRKAe9FPmiavPUYgQEdOqy3j5wXjOZI,9842
|
39
|
+
droidrun/cli/main.py,sha256=yW6TU7nk91zuM9QFHfoJz7wbTdJPNHnpoiXZfuA3gbo,18392
|
39
40
|
droidrun/macro/__init__.py,sha256=333sMt19mA8sfQa4qPKbbCmr8Ej3nvpdxGXUZtVTEqM,344
|
40
41
|
droidrun/macro/__main__.py,sha256=-zj42Bj7309oLPZbNsxZeNwIDaEe7Th1I4zF8yAHasw,193
|
41
42
|
droidrun/macro/cli.py,sha256=GaL1wVWVE_AT0OxHgOJyW-q_kVu4PQ76KZKeYMQdoEk,9057
|
42
43
|
droidrun/macro/replay.py,sha256=q_3ZcHVjvsdDfS2xyt_vuuwXGt9_1t38JD1cPsjzIfU,10764
|
43
44
|
droidrun/telemetry/__init__.py,sha256=D4Mp02iGJH2Tjpv42Bzyo6_WC3NWj9Qy9hQPWFaCkhA,234
|
44
|
-
droidrun/telemetry/events.py,sha256=
|
45
|
+
droidrun/telemetry/events.py,sha256=OIYIs5stZtKz0g82W8WFE_h6q3jh0vUhc9_CwNTJ3a4,529
|
45
46
|
droidrun/telemetry/tracker.py,sha256=Ljue6zivX8KnadXI9DivrayuWxAnUwbJKvCvNtY1Y4Y,2717
|
46
47
|
droidrun/tools/__init__.py,sha256=9ReauavtSKDQG9ya9_Fr9O0TQnDFixgOPaP5n82_iEk,271
|
47
|
-
droidrun/tools/adb.py,sha256=
|
48
|
+
droidrun/tools/adb.py,sha256=91HmimUJtvGyMcE2HEDJLvTQt95ZYNCXWAJlOs4X9HQ,38302
|
48
49
|
droidrun/tools/ios.py,sha256=imzojiS6gqz4IKexUEz1ga7-flSOaC5QRpHIJTwcgSQ,21807
|
49
|
-
droidrun/tools/tools.py,sha256=
|
50
|
-
droidrun-0.3.
|
51
|
-
droidrun-0.3.
|
52
|
-
droidrun-0.3.
|
53
|
-
droidrun-0.3.
|
54
|
-
droidrun-0.3.
|
50
|
+
droidrun/tools/tools.py,sha256=Swlzo9A8B8YcgU6XNHRG1LNpo_-RljzhyVhnEYYm-G4,4772
|
51
|
+
droidrun-0.3.7.dist-info/METADATA,sha256=0bBaBhRe06W_72e-eRtnnHDTTK2KXEQZx82gcaBhuYo,6860
|
52
|
+
droidrun-0.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
53
|
+
droidrun-0.3.7.dist-info/entry_points.txt,sha256=o259U66js8TIybQ7zs814Oe_LQ_GpZsp6a9Cr-xm5zE,51
|
54
|
+
droidrun-0.3.7.dist-info/licenses/LICENSE,sha256=s-uxn9qChu-kFdRXUp6v_0HhsaJ_5OANmfNOFVm2zdk,1069
|
55
|
+
droidrun-0.3.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|