droidrun 0.3.8__tar.gz → 0.3.9__tar.gz
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-0.3.8 → droidrun-0.3.9}/.gitignore +0 -3
- droidrun-0.3.9/.python-version +1 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/PKG-INFO +9 -6
- {droidrun-0.3.8 → droidrun-0.3.9}/README.md +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/docs.json +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/cli.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/gemini.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/ollama.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/openailike.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/overview.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/quickstart.mdx +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/codeact/codeact_agent.py +48 -10
- droidrun-0.3.9/droidrun/agent/common/constants.py +2 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/common/events.py +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/droid/droid_agent.py +2 -4
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/planner/planner_agent.py +36 -14
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/usage.py +4 -2
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/executer.py +1 -1
- {droidrun-0.3.8 → droidrun-0.3.9}/pyproject.toml +8 -5
- droidrun-0.3.9/uv.lock +4125 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/.github/workflows/bounty.yml +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/.github/workflows/publish.yml +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/CHANGELOG.md +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/CONTRIBUTING.md +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/LICENSE +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/MANIFEST.in +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/.generated-files.txt +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/favicon.png +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/logo/dark.svg +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/logo/light.svg +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v1/concepts/agent.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v1/concepts/android-control.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v1/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v1/overview.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v1/quickstart.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/concepts/agent.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/concepts/android-control.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/concepts/planning.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/concepts/tracing.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/overview.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v2/quickstart.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/concepts/agent.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/concepts/android-tools.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/concepts/models.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/overview.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/guides/telemetry.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/images/portal_apk.png +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/sdk/adb-tools.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/sdk/base-tools.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/sdk/droid-agent.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/docs/v3/sdk/ios-tools.mdx +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/__main__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/codeact/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/codeact/events.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/codeact/prompts.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/common/default.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/agent_persona.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/context_injection_manager.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/episodic_memory.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/personas/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/personas/app_starter.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/personas/big_agent.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/personas/default.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/personas/ui_expert.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/reflection.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/context/task_manager.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/droid/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/droid/events.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/oneflows/reflector.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/planner/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/planner/events.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/planner/prompts.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/async_utils.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/chat_utils.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/llm_picker.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/agent/utils/trajectory.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/cli/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/cli/logs.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/cli/main.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/macro/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/macro/__main__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/macro/cli.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/macro/replay.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/portal.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/telemetry/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/telemetry/events.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/telemetry/tracker.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/tools/__init__.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/tools/adb.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/tools/ios.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/droidrun/tools/tools.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/gen-docs-sdk-ref.sh +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/setup.py +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/static/droidrun-dark.png +0 -0
- {droidrun-0.3.8 → droidrun-0.3.9}/static/droidrun.png +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
3.13.7
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: droidrun
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.9
|
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
|
@@ -16,6 +16,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
19
21
|
Classifier: Topic :: Communications :: Chat
|
20
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
21
23
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
@@ -28,13 +30,14 @@ Classifier: Topic :: Utilities
|
|
28
30
|
Requires-Python: >=3.11
|
29
31
|
Requires-Dist: adbutils>=2.10.2
|
30
32
|
Requires-Dist: apkutils==2.0.0
|
31
|
-
Requires-Dist: llama-index
|
32
|
-
Requires-Dist:
|
33
|
-
Requires-Dist:
|
33
|
+
Requires-Dist: llama-index-llms-google-genai>=0.6.2
|
34
|
+
Requires-Dist: llama-index==0.14.4
|
35
|
+
Requires-Dist: posthog>=6.7.6
|
36
|
+
Requires-Dist: pydantic>=2.11.10
|
34
37
|
Requires-Dist: rich>=14.1.0
|
35
38
|
Provides-Extra: anthropic
|
36
39
|
Requires-Dist: anthropic>=0.67.0; extra == 'anthropic'
|
37
|
-
Requires-Dist: llama-index-llms-anthropic
|
40
|
+
Requires-Dist: llama-index-llms-anthropic<0.9.0,>=0.8.6; extra == 'anthropic'
|
38
41
|
Provides-Extra: deepseek
|
39
42
|
Requires-Dist: llama-index-llms-deepseek>=0.2.1; extra == 'deepseek'
|
40
43
|
Provides-Extra: dev
|
@@ -88,7 +91,7 @@ DroidRun is a powerful framework for controlling Android and iOS devices through
|
|
88
91
|
## 📦 Installation
|
89
92
|
|
90
93
|
```bash
|
91
|
-
pip install droidrun[google,anthropic,openai,deepseek,ollama,dev]
|
94
|
+
pip install 'droidrun[google,anthropic,openai,deepseek,ollama,dev]'
|
92
95
|
```
|
93
96
|
|
94
97
|
## 🚀 Quickstart
|
@@ -33,7 +33,7 @@ DroidRun is a powerful framework for controlling Android and iOS devices through
|
|
33
33
|
## 📦 Installation
|
34
34
|
|
35
35
|
```bash
|
36
|
-
pip install droidrun[google,anthropic,openai,deepseek,ollama,dev]
|
36
|
+
pip install 'droidrun[google,anthropic,openai,deepseek,ollama,dev]'
|
37
37
|
```
|
38
38
|
|
39
39
|
## 🚀 Quickstart
|
@@ -13,7 +13,7 @@ DroidRun lets you control Android devices using natural language and LLM agents.
|
|
13
13
|
<Steps>
|
14
14
|
<Step title="Install DroidRun and its dependencies. Choose which ever provider you'd like to use.">
|
15
15
|
```sh
|
16
|
-
pip install droidrun[google,anthropic,openai,deepseek,ollama,dev]
|
16
|
+
pip install 'droidrun[google,anthropic,openai,deepseek,ollama,dev]'
|
17
17
|
```
|
18
18
|
</Step>
|
19
19
|
<Step title="Ensure your Android device is connected and the DroidRun Portal is installed">
|
@@ -29,7 +29,7 @@ DroidRun empowers you to automate sophisticated mobile workflows through intelli
|
|
29
29
|
<Card icon="mobile" title="Physical Device" href="/v3/quickstart" arrow>
|
30
30
|
- Connect your own physical Android device for direct automation
|
31
31
|
</Card>
|
32
|
-
<Card icon="cloud" title="Cloud Environment" href="https://droidrun.ai
|
32
|
+
<Card icon="cloud" title="Cloud Environment" href="https://cloud.droidrun.ai">
|
33
33
|
- Access our managed cloud environment for instant mobile app automation without any setup.
|
34
34
|
</Card>
|
35
35
|
</CardGroup>
|
@@ -28,7 +28,7 @@ Before installing DroidRun, ensure you have:
|
|
28
28
|
### Install from PyPI
|
29
29
|
Choose which ever provider you'd like to use. If you encounter dependency resolution issues use [`uv`](https://docs.astral.sh/uv/getting-started/installation/).
|
30
30
|
```bash
|
31
|
-
pip install droidrun[google,anthropic,openai,deepseek,ollama,dev]
|
31
|
+
pip install 'droidrun[google,anthropic,openai,deepseek,ollama,dev]'
|
32
32
|
```
|
33
33
|
|
34
34
|
### Setup the Portal APK
|
@@ -18,7 +18,8 @@ from droidrun.agent.codeact.events import (
|
|
18
18
|
TaskThinkingEvent,
|
19
19
|
EpisodicMemoryEvent,
|
20
20
|
)
|
21
|
-
from droidrun.agent.common.
|
21
|
+
from droidrun.agent.common.constants import LLM_HISTORY_LIMIT
|
22
|
+
from droidrun.agent.common.events import RecordUIStateEvent, ScreenshotEvent
|
22
23
|
from droidrun.agent.usage import get_usage_from_response
|
23
24
|
from droidrun.agent.utils import chat_utils
|
24
25
|
from droidrun.agent.utils.executer import SimpleCodeExecutor
|
@@ -109,7 +110,8 @@ class CodeActAgent(Workflow):
|
|
109
110
|
"""Prepare chat history from user input."""
|
110
111
|
logger.info("💬 Preparing chat for task execution...")
|
111
112
|
|
112
|
-
|
113
|
+
|
114
|
+
self.chat_memory: Memory = await ctx.store.get(
|
113
115
|
"chat_memory", default=Memory.from_defaults()
|
114
116
|
)
|
115
117
|
|
@@ -135,7 +137,7 @@ class CodeActAgent(Workflow):
|
|
135
137
|
|
136
138
|
await self.chat_memory.aput(self.user_message)
|
137
139
|
|
138
|
-
await ctx.set("chat_memory", self.chat_memory)
|
140
|
+
await ctx.store.set("chat_memory", self.chat_memory)
|
139
141
|
input_messages = self.chat_memory.get_all()
|
140
142
|
return TaskInputEvent(input=input_messages)
|
141
143
|
|
@@ -162,7 +164,7 @@ class CodeActAgent(Workflow):
|
|
162
164
|
model = self.llm.class_name()
|
163
165
|
|
164
166
|
if "remember" in self.tool_list and self.remembered_info:
|
165
|
-
await ctx.set("remembered_info", self.remembered_info)
|
167
|
+
await ctx.store.set("remembered_info", self.remembered_info)
|
166
168
|
chat_history = await chat_utils.add_memory_block(self.remembered_info, chat_history)
|
167
169
|
|
168
170
|
for context in self.required_context:
|
@@ -171,7 +173,7 @@ class CodeActAgent(Workflow):
|
|
171
173
|
screenshot = (self.tools.take_screenshot())[1]
|
172
174
|
ctx.write_event_to_stream(ScreenshotEvent(screenshot=screenshot))
|
173
175
|
|
174
|
-
await ctx.set("screenshot", screenshot)
|
176
|
+
await ctx.store.set("screenshot", screenshot)
|
175
177
|
if model == "DeepSeek":
|
176
178
|
logger.warning(
|
177
179
|
"[yellow]DeepSeek doesnt support images. Disabling screenshots[/]"
|
@@ -182,7 +184,7 @@ class CodeActAgent(Workflow):
|
|
182
184
|
if context == "ui_state":
|
183
185
|
try:
|
184
186
|
state = self.tools.get_state()
|
185
|
-
await ctx.set("ui_state", state["a11y_tree"])
|
187
|
+
await ctx.store.set("ui_state", state["a11y_tree"])
|
186
188
|
ctx.write_event_to_stream(RecordUIStateEvent(ui_state=state["a11y_tree"]))
|
187
189
|
chat_history = await chat_utils.add_ui_text_block(
|
188
190
|
state["a11y_tree"], chat_history
|
@@ -320,7 +322,7 @@ class CodeActAgent(Workflow):
|
|
320
322
|
async def finalize(self, ev: TaskEndEvent, ctx: Context) -> StopEvent:
|
321
323
|
"""Finalize the workflow."""
|
322
324
|
self.tools.finished = False
|
323
|
-
await ctx.set("chat_memory", self.chat_memory)
|
325
|
+
await ctx.store.set("chat_memory", self.chat_memory)
|
324
326
|
|
325
327
|
# Add final state observation to episodic memory
|
326
328
|
if self.vision:
|
@@ -347,14 +349,15 @@ class CodeActAgent(Workflow):
|
|
347
349
|
self, ctx: Context, chat_history: List[ChatMessage]
|
348
350
|
) -> ChatResponse | None:
|
349
351
|
logger.debug("🔍 Getting LLM response...")
|
350
|
-
|
352
|
+
limited_history = self._limit_history(chat_history)
|
353
|
+
messages_to_send = [self.system_prompt] + limited_history
|
351
354
|
messages_to_send = [chat_utils.message_copy(msg) for msg in messages_to_send]
|
352
355
|
try:
|
353
356
|
response = await self.llm.achat(messages=messages_to_send)
|
354
357
|
logger.debug("🔍 Received LLM response.")
|
355
358
|
|
356
359
|
filtered_chat_history = []
|
357
|
-
for msg in
|
360
|
+
for msg in limited_history:
|
358
361
|
filtered_msg = chat_utils.message_copy(msg)
|
359
362
|
if hasattr(filtered_msg, "blocks") and filtered_msg.blocks:
|
360
363
|
filtered_msg.blocks = [
|
@@ -379,9 +382,10 @@ class CodeActAgent(Workflow):
|
|
379
382
|
chat_history=chat_history_str,
|
380
383
|
response=response_str,
|
381
384
|
timestamp=time.time(),
|
382
|
-
screenshot=(await ctx.get("screenshot", None))
|
385
|
+
screenshot=(await ctx.store.get("screenshot", None))
|
383
386
|
)
|
384
387
|
|
388
|
+
|
385
389
|
self.episodic_memory.steps.append(step)
|
386
390
|
|
387
391
|
assert hasattr(
|
@@ -403,12 +407,46 @@ class CodeActAgent(Workflow):
|
|
403
407
|
time.sleep(40)
|
404
408
|
logger.debug("🔍 Retrying call to LLM...")
|
405
409
|
response = await self.llm.achat(messages=messages_to_send)
|
410
|
+
elif (
|
411
|
+
self.llm.class_name() == "Anthropic_LLM"
|
412
|
+
and "overloaded_error" in str(e)
|
413
|
+
):
|
414
|
+
# Use exponential backoff for Anthropic errors
|
415
|
+
if not hasattr(self, '_anthropic_retry_count'):
|
416
|
+
self._anthropic_retry_count = 0
|
417
|
+
self._anthropic_retry_count += 1
|
418
|
+
seconds = min(2 ** self._anthropic_retry_count, 60) # Cap at 60 seconds
|
419
|
+
logger.error(f"Anthropic overload error. Retrying in {seconds} seconds... (attempt {self._anthropic_retry_count})")
|
420
|
+
time.sleep(seconds)
|
421
|
+
logger.debug("🔍 Retrying call to LLM...")
|
422
|
+
response = await self.llm.achat(messages=messages_to_send)
|
423
|
+
self._anthropic_retry_count = 0 # Reset on success
|
406
424
|
else:
|
407
425
|
logger.error(f"Could not get an answer from LLM: {repr(e)}")
|
408
426
|
raise e
|
409
427
|
logger.debug(" - Received response from LLM.")
|
410
428
|
return response
|
411
429
|
|
430
|
+
def _limit_history(
|
431
|
+
self, chat_history: List[ChatMessage]
|
432
|
+
) -> List[ChatMessage]:
|
433
|
+
if LLM_HISTORY_LIMIT <= 0:
|
434
|
+
return chat_history
|
435
|
+
|
436
|
+
max_messages = LLM_HISTORY_LIMIT * 2
|
437
|
+
if len(chat_history) <= max_messages:
|
438
|
+
return chat_history
|
439
|
+
|
440
|
+
preserved_head: List[ChatMessage] = []
|
441
|
+
if chat_history and chat_history[0].role == "user":
|
442
|
+
preserved_head = [chat_history[0]]
|
443
|
+
|
444
|
+
tail = chat_history[-max_messages:]
|
445
|
+
if preserved_head and preserved_head[0] in tail:
|
446
|
+
preserved_head = []
|
447
|
+
|
448
|
+
return preserved_head + tail
|
449
|
+
|
412
450
|
async def _add_final_state_observation(self, ctx: Context) -> None:
|
413
451
|
"""Add the current UI state and screenshot as the final observation step."""
|
414
452
|
try:
|
@@ -165,7 +165,6 @@ class DroidAgent(Workflow):
|
|
165
165
|
timeout=timeout,
|
166
166
|
debug=debug,
|
167
167
|
)
|
168
|
-
self.add_workflows(planner_agent=self.planner_agent)
|
169
168
|
self.max_codeact_steps = 5
|
170
169
|
|
171
170
|
if self.reflection:
|
@@ -335,7 +334,6 @@ class DroidAgent(Workflow):
|
|
335
334
|
self,
|
336
335
|
ctx: Context,
|
337
336
|
ev: ReasoningLogicEvent,
|
338
|
-
planner_agent: Workflow = MockWorkflow(),
|
339
337
|
) -> FinalizeEvent | CodeActExecuteEvent:
|
340
338
|
try:
|
341
339
|
if self.step_counter >= self.max_steps:
|
@@ -352,7 +350,7 @@ class DroidAgent(Workflow):
|
|
352
350
|
self.step_counter += 1
|
353
351
|
|
354
352
|
if ev.reflection:
|
355
|
-
handler = planner_agent.run(
|
353
|
+
handler = self.planner_agent.run(
|
356
354
|
remembered_info=self.tools_instance.memory, reflection=ev.reflection
|
357
355
|
)
|
358
356
|
else:
|
@@ -365,7 +363,7 @@ class DroidAgent(Workflow):
|
|
365
363
|
|
366
364
|
logger.debug(f"Planning step {self.step_counter}/{self.max_steps}")
|
367
365
|
|
368
|
-
handler = planner_agent.run(
|
366
|
+
handler = self.planner_agent.run(
|
369
367
|
remembered_info=self.tools_instance.memory, reflection=None
|
370
368
|
)
|
371
369
|
|
@@ -18,7 +18,8 @@ from droidrun.agent.utils.executer import SimpleCodeExecutor
|
|
18
18
|
from droidrun.agent.utils import chat_utils
|
19
19
|
from droidrun.agent.context.task_manager import TaskManager
|
20
20
|
from droidrun.tools import Tools
|
21
|
-
from droidrun.agent.common.
|
21
|
+
from droidrun.agent.common.constants import LLM_HISTORY_LIMIT
|
22
|
+
from droidrun.agent.common.events import RecordUIStateEvent, ScreenshotEvent
|
22
23
|
from droidrun.agent.planner.events import (
|
23
24
|
PlanInputEvent,
|
24
25
|
PlanCreatedEvent,
|
@@ -97,7 +98,7 @@ class PlannerAgent(Workflow):
|
|
97
98
|
async def prepare_chat(self, ctx: Context, ev: StartEvent) -> PlanInputEvent:
|
98
99
|
logger.info("💬 Preparing planning session...")
|
99
100
|
|
100
|
-
self.chat_memory: Memory = await ctx.get(
|
101
|
+
self.chat_memory: Memory = await ctx.store.get(
|
101
102
|
"chat_memory", default=Memory.from_defaults()
|
102
103
|
)
|
103
104
|
await self.chat_memory.aput(self.user_message)
|
@@ -134,19 +135,19 @@ class PlannerAgent(Workflow):
|
|
134
135
|
if self.vision:
|
135
136
|
screenshot = (self.tools_instance.take_screenshot())[1]
|
136
137
|
ctx.write_event_to_stream(ScreenshotEvent(screenshot=screenshot))
|
137
|
-
await ctx.set("screenshot", screenshot)
|
138
|
+
await ctx.store.set("screenshot", screenshot)
|
138
139
|
|
139
140
|
try:
|
140
141
|
state = self.tools_instance.get_state()
|
141
|
-
await ctx.set("ui_state", state["a11y_tree"])
|
142
|
-
await ctx.set("phone_state", state["phone_state"])
|
142
|
+
await ctx.store.set("ui_state", state["a11y_tree"])
|
143
|
+
await ctx.store.set("phone_state", state["phone_state"])
|
143
144
|
ctx.write_event_to_stream(RecordUIStateEvent(ui_state=state["a11y_tree"]))
|
144
145
|
except Exception as e:
|
145
146
|
logger.warning(f"⚠️ Error retrieving state from the connected device. Is the Accessibility Service enabled?")
|
146
147
|
|
147
148
|
|
148
|
-
await ctx.set("remembered_info", self.remembered_info)
|
149
|
-
await ctx.set("reflection", self.reflection)
|
149
|
+
await ctx.store.set("remembered_info", self.remembered_info)
|
150
|
+
await ctx.store.set("reflection", self.reflection)
|
150
151
|
|
151
152
|
response = await self._get_llm_response(ctx, chat_history)
|
152
153
|
try:
|
@@ -237,7 +238,7 @@ wrap your code inside this:
|
|
237
238
|
@step
|
238
239
|
async def finalize(self, ev: PlanCreatedEvent, ctx: Context) -> StopEvent:
|
239
240
|
"""Finalize the workflow."""
|
240
|
-
await ctx.set("chat_memory", self.chat_memory)
|
241
|
+
await ctx.store.set("chat_memory", self.chat_memory)
|
241
242
|
|
242
243
|
result = {}
|
243
244
|
result.update(
|
@@ -263,7 +264,7 @@ wrap your code inside this:
|
|
263
264
|
)
|
264
265
|
else:
|
265
266
|
chat_history = await chat_utils.add_screenshot_image_block(
|
266
|
-
await ctx.get("screenshot"), chat_history
|
267
|
+
await ctx.store.get("screenshot"), chat_history
|
267
268
|
)
|
268
269
|
|
269
270
|
|
@@ -275,18 +276,19 @@ wrap your code inside this:
|
|
275
276
|
chat_history,
|
276
277
|
)
|
277
278
|
|
278
|
-
remembered_info = await ctx.get("remembered_info", default=None)
|
279
|
+
remembered_info = await ctx.store.get("remembered_info", default=None)
|
279
280
|
if remembered_info:
|
280
281
|
chat_history = await chat_utils.add_memory_block(remembered_info, chat_history)
|
281
282
|
|
282
|
-
reflection = await ctx.get("reflection", None)
|
283
|
+
reflection = await ctx.store.get("reflection", None)
|
283
284
|
if reflection:
|
284
285
|
chat_history = await chat_utils.add_reflection_summary(reflection, chat_history)
|
285
286
|
|
286
|
-
chat_history = await chat_utils.add_phone_state_block(await ctx.get("phone_state"), chat_history)
|
287
|
-
chat_history = await chat_utils.add_ui_text_block(await ctx.get("ui_state"), chat_history)
|
287
|
+
chat_history = await chat_utils.add_phone_state_block(await ctx.store.get("phone_state"), chat_history)
|
288
|
+
chat_history = await chat_utils.add_ui_text_block(await ctx.store.get("ui_state"), chat_history)
|
288
289
|
|
289
|
-
|
290
|
+
limited_history = self._limit_history(chat_history)
|
291
|
+
messages_to_send = [self.system_message] + limited_history
|
290
292
|
messages_to_send = [
|
291
293
|
chat_utils.message_copy(msg) for msg in messages_to_send
|
292
294
|
]
|
@@ -302,3 +304,23 @@ wrap your code inside this:
|
|
302
304
|
except Exception as e:
|
303
305
|
logger.error(f"Could not get an answer from LLM: {repr(e)}")
|
304
306
|
raise e
|
307
|
+
|
308
|
+
def _limit_history(
|
309
|
+
self, chat_history: List[ChatMessage]
|
310
|
+
) -> List[ChatMessage]:
|
311
|
+
if LLM_HISTORY_LIMIT <= 0:
|
312
|
+
return chat_history
|
313
|
+
|
314
|
+
max_messages = LLM_HISTORY_LIMIT * 2
|
315
|
+
if len(chat_history) <= max_messages:
|
316
|
+
return chat_history
|
317
|
+
|
318
|
+
preserved_head: List[ChatMessage] = []
|
319
|
+
if chat_history and chat_history[0].role == "user":
|
320
|
+
preserved_head = [chat_history[0]]
|
321
|
+
|
322
|
+
tail = chat_history[-max_messages:]
|
323
|
+
if preserved_head and preserved_head[0] in tail:
|
324
|
+
preserved_head = []
|
325
|
+
|
326
|
+
return preserved_head + tail
|
@@ -12,7 +12,9 @@ logger = logging.getLogger("droidrun")
|
|
12
12
|
SUPPORTED_PROVIDERS = [
|
13
13
|
"Gemini",
|
14
14
|
"GoogleGenAI",
|
15
|
+
"GenAI",
|
15
16
|
"OpenAI",
|
17
|
+
"openai_llm",
|
16
18
|
"Anthropic",
|
17
19
|
"Ollama",
|
18
20
|
"DeepSeek",
|
@@ -32,14 +34,14 @@ def get_usage_from_response(provider: str, chat_rsp: ChatResponse) -> UsageResul
|
|
32
34
|
|
33
35
|
print(f"rsp: {rsp.__class__.__name__}")
|
34
36
|
|
35
|
-
if provider == "Gemini" or provider == "GoogleGenAI":
|
37
|
+
if provider == "Gemini" or provider == "GoogleGenAI" or provider == "GenAI":
|
36
38
|
return UsageResult(
|
37
39
|
request_tokens=rsp["usage_metadata"]["prompt_token_count"],
|
38
40
|
response_tokens=rsp["usage_metadata"]["candidates_token_count"],
|
39
41
|
total_tokens=rsp["usage_metadata"]["total_token_count"],
|
40
42
|
requests=1,
|
41
43
|
)
|
42
|
-
elif provider == "OpenAI":
|
44
|
+
elif provider == "OpenAI" or provider == "openai_llm":
|
43
45
|
from openai.types import CompletionUsage as OpenAIUsage
|
44
46
|
|
45
47
|
usage: OpenAIUsage = rsp.usage
|
@@ -98,7 +98,7 @@ class SimpleCodeExecutor:
|
|
98
98
|
str: Output from the execution, including print statements.
|
99
99
|
"""
|
100
100
|
# Update UI elements before execution
|
101
|
-
self.globals['ui_state'] = await ctx.get("ui_state", None)
|
101
|
+
self.globals['ui_state'] = await ctx.store.get("ui_state", None)
|
102
102
|
self.globals['step_screenshots'] = []
|
103
103
|
self.globals['step_ui_states'] = []
|
104
104
|
|
@@ -1,15 +1,16 @@
|
|
1
1
|
[project]
|
2
2
|
name = "droidrun"
|
3
|
-
version = "0.3.
|
3
|
+
version = "0.3.9"
|
4
4
|
description = "A framework for controlling Android devices through LLM agents"
|
5
5
|
authors = [{ name = "Niels Schmidt", email = "niels.schmidt@droidrun.ai" }]
|
6
6
|
dependencies = [
|
7
7
|
"adbutils>=2.10.2",
|
8
8
|
# dependency of adbutils[apk]
|
9
9
|
"apkutils==2.0.0",
|
10
|
-
"llama-index==0.
|
11
|
-
"
|
12
|
-
"
|
10
|
+
"llama-index==0.14.4",
|
11
|
+
"llama-index-llms-google-genai>=0.6.2",
|
12
|
+
"posthog>=6.7.6",
|
13
|
+
"pydantic>=2.11.10",
|
13
14
|
"rich>=14.1.0",
|
14
15
|
]
|
15
16
|
requires-python = ">=3.11"
|
@@ -24,6 +25,8 @@ classifiers = [
|
|
24
25
|
"Programming Language :: Python :: 3",
|
25
26
|
"Programming Language :: Python :: 3.10",
|
26
27
|
"Programming Language :: Python :: 3.11",
|
28
|
+
"Programming Language :: Python :: 3.12",
|
29
|
+
"Programming Language :: Python :: 3.13",
|
27
30
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
28
31
|
"Topic :: Software Development :: Testing",
|
29
32
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
@@ -43,7 +46,7 @@ Documentation = "https://docs.droidrun.ai/"
|
|
43
46
|
[project.optional-dependencies]
|
44
47
|
anthropic = [
|
45
48
|
"anthropic>=0.67.0",
|
46
|
-
"llama-index-llms-anthropic>=0.8.6",
|
49
|
+
"llama-index-llms-anthropic>=0.8.6,<0.9.0",
|
47
50
|
]
|
48
51
|
openai = [
|
49
52
|
"openai>=1.99.1",
|