droidrun 0.3.10.dev14__tar.gz → 0.4.0.dev1__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.10.dev14 → droidrun-0.4.0.dev1}/PKG-INFO +2 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/__main__.py +2 -1
- droidrun-0.4.0.dev1/droidrun/agent/__init__.py +6 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/__init__.py +1 -3
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/codeact_agent.py +71 -35
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/events.py +4 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/constants.py +1 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/events.py +16 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/__init__.py +1 -6
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/task_manager.py +27 -20
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/__init__.py +1 -4
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/droid_agent.py +87 -80
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/events.py +12 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/__init__.py +5 -2
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/events.py +2 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/executor_agent.py +95 -50
- droidrun-0.4.0.dev1/droidrun/agent/executor/prompts.py +51 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/events.py +2 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/manager_agent.py +140 -100
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/prompts.py +12 -5
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/app_starter_workflow.py +7 -5
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/text_manipulator.py +25 -6
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/events.py +5 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/scripter_agent.py +57 -26
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/usage.py +4 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/chat_utils.py +79 -44
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/device_state_formatter.py +25 -21
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/executer.py +23 -15
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/inference.py +8 -9
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/llm_loader.py +24 -21
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/llm_picker.py +7 -3
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/message_utils.py +6 -6
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/tools.py +20 -15
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/trajectory.py +2 -4
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/composite_provider.py +8 -4
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/local_provider.py +2 -3
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/server_provider.py +5 -11
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/logs.py +7 -2
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/main.py +67 -50
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/config_manager.py +50 -16
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/path_resolver.py +1 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/prompt_loader.py +2 -2
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/safe_execution.py +80 -28
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/__init__.py +1 -5
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/cli.py +55 -12
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/portal.py +7 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/phoenix.py +42 -18
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/tracker.py +3 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/adb.py +34 -15
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/ios.py +6 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/portal_client.py +67 -46
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/tools.py +23 -8
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/pyproject.toml +2 -1
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/uv.lock +3 -1
- droidrun-0.3.10.dev14/droidrun/agent/__init__.py +0 -6
- droidrun-0.3.10.dev14/droidrun/agent/executor/prompts.py +0 -34
- {droidrun-0.3.10.dev14/.github → droidrun-0.4.0.dev1/.github/workflows}/black.yml +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.github/workflows/bounty.yml +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.github/workflows/publish.yml +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.gitignore +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.python-version +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/CHANGELOG.md +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/CONTRIBUTING.md +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/LICENSE +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/MANIFEST.in +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/README.md +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/.generated-files.txt +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/docs.json +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/favicon.png +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/logo/dark.svg +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/logo/light.svg +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/android-control.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/overview.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/android-control.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/planning.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/tracing.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/overview.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/android-tools.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/models.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/cli.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/gemini.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/ollama.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/openailike.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/overview.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/telemetry.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/images/portal_apk.png +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/overview.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/adb-tools.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/base-tools.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/droid-agent.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/ios-tools.mdx +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/episodic_memory.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/async_utils.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/app_card_provider.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/README.md +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/app_cards.json +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/gmail.md +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/codeact/system.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/codeact/user.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/executor/rev1.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/executor/system.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/manager/rev1.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/manager/system.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/scripter/system.jinja2 +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_example.yaml +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/__main__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/replay.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/events.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/__init__.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/gen-docs-sdk-ref.sh +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/setup.py +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/static/droidrun-dark.png +0 -0
- {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/static/droidrun.png +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: droidrun
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0.dev1
|
|
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
|
|
@@ -30,6 +30,7 @@ Requires-Dist: adbutils>=2.10.2
|
|
|
30
30
|
Requires-Dist: apkutils==2.0.0
|
|
31
31
|
Requires-Dist: arize-phoenix>=12.3.0
|
|
32
32
|
Requires-Dist: httpx>=0.27.0
|
|
33
|
+
Requires-Dist: llama-index-callbacks-arize-phoenix>=0.6.1
|
|
33
34
|
Requires-Dist: llama-index==0.14.4
|
|
34
35
|
Requires-Dist: posthog>=6.7.6
|
|
35
36
|
Requires-Dist: pydantic>=2.11.10
|
|
@@ -93,7 +93,11 @@ class CodeActAgent(Workflow):
|
|
|
93
93
|
for action_name, signature in merged_signatures.items():
|
|
94
94
|
func = signature["function"]
|
|
95
95
|
|
|
96
|
-
self.tool_list[action_name] =
|
|
96
|
+
self.tool_list[action_name] = (
|
|
97
|
+
lambda *args, f=func, ti=tools_instance, **kwargs: f(
|
|
98
|
+
ti, *args, **kwargs
|
|
99
|
+
)
|
|
100
|
+
)
|
|
97
101
|
|
|
98
102
|
self.tool_list["remember"] = tools_instance.remember
|
|
99
103
|
self.tool_list["complete"] = tools_instance.complete
|
|
@@ -103,13 +107,17 @@ class CodeActAgent(Workflow):
|
|
|
103
107
|
custom_descriptions = build_custom_tool_descriptions(custom_tools or {})
|
|
104
108
|
if custom_descriptions:
|
|
105
109
|
self.tool_descriptions += "\n" + custom_descriptions
|
|
106
|
-
self.tool_descriptions +=
|
|
107
|
-
|
|
110
|
+
self.tool_descriptions += (
|
|
111
|
+
"\n- remember(information: str): Remember information for later use"
|
|
112
|
+
)
|
|
113
|
+
self.tool_descriptions += (
|
|
114
|
+
"\n- complete(success: bool, reason: str): Mark task as complete"
|
|
115
|
+
)
|
|
108
116
|
|
|
109
117
|
# Load prompts from config
|
|
110
118
|
system_prompt_text = PromptLoader.load_prompt(
|
|
111
119
|
agent_config.get_codeact_system_prompt_path(),
|
|
112
|
-
{"tool_descriptions": self.tool_descriptions}
|
|
120
|
+
{"tool_descriptions": self.tool_descriptions},
|
|
113
121
|
)
|
|
114
122
|
self.system_prompt = ChatMessage(role="system", content=system_prompt_text)
|
|
115
123
|
|
|
@@ -123,10 +131,22 @@ class CodeActAgent(Workflow):
|
|
|
123
131
|
tools=self.tool_list,
|
|
124
132
|
globals={"__builtins__": __builtins__},
|
|
125
133
|
safe_mode=safe_mode,
|
|
126
|
-
allowed_modules=
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
allowed_modules=(
|
|
135
|
+
safe_config.get_allowed_modules() if safe_config and safe_mode else None
|
|
136
|
+
),
|
|
137
|
+
blocked_modules=(
|
|
138
|
+
safe_config.get_blocked_modules() if safe_config and safe_mode else None
|
|
139
|
+
),
|
|
140
|
+
allowed_builtins=(
|
|
141
|
+
safe_config.get_allowed_builtins()
|
|
142
|
+
if safe_config and safe_mode
|
|
143
|
+
else None
|
|
144
|
+
),
|
|
145
|
+
blocked_builtins=(
|
|
146
|
+
safe_config.get_blocked_builtins()
|
|
147
|
+
if safe_config and safe_mode
|
|
148
|
+
else None
|
|
149
|
+
),
|
|
130
150
|
)
|
|
131
151
|
|
|
132
152
|
logger.info("✅ CodeActAgent initialized successfully.")
|
|
@@ -136,7 +156,6 @@ class CodeActAgent(Workflow):
|
|
|
136
156
|
"""Prepare chat history from user input."""
|
|
137
157
|
logger.info("💬 Preparing chat for task execution...")
|
|
138
158
|
|
|
139
|
-
|
|
140
159
|
self.chat_memory: Memory = await ctx.store.get(
|
|
141
160
|
"chat_memory", default=Memory.from_defaults()
|
|
142
161
|
)
|
|
@@ -152,8 +171,7 @@ class CodeActAgent(Workflow):
|
|
|
152
171
|
|
|
153
172
|
# Format user prompt with goal
|
|
154
173
|
user_prompt_text = PromptLoader.load_prompt(
|
|
155
|
-
self.agent_config.get_codeact_user_prompt_path(),
|
|
156
|
-
{"goal": goal}
|
|
174
|
+
self.agent_config.get_codeact_user_prompt_path(), {"goal": goal}
|
|
157
175
|
)
|
|
158
176
|
self.user_message = ChatMessage(role="user", content=user_prompt_text)
|
|
159
177
|
|
|
@@ -165,7 +183,6 @@ The code you provided will be executed below.
|
|
|
165
183
|
Now, describe the next step you will take to address the original goal: {goal}"""
|
|
166
184
|
self.no_thoughts_prompt = ChatMessage(role="user", content=no_thoughts_text)
|
|
167
185
|
|
|
168
|
-
|
|
169
186
|
await self.chat_memory.aput(self.user_message)
|
|
170
187
|
|
|
171
188
|
await ctx.store.set("chat_memory", self.chat_memory)
|
|
@@ -195,7 +212,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
195
212
|
|
|
196
213
|
if "remember" in self.tool_list and self.remembered_info:
|
|
197
214
|
await ctx.store.set("remembered_info", self.remembered_info)
|
|
198
|
-
chat_history = await chat_utils.add_memory_block(
|
|
215
|
+
chat_history = await chat_utils.add_memory_block(
|
|
216
|
+
self.remembered_info, chat_history
|
|
217
|
+
)
|
|
199
218
|
|
|
200
219
|
# Always capture screenshot for trajectory
|
|
201
220
|
screenshot = (self.tools.take_screenshot())[1]
|
|
@@ -204,7 +223,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
204
223
|
|
|
205
224
|
# Add screenshot to chat only if vision enabled
|
|
206
225
|
if self.vision and model != "DeepSeek":
|
|
207
|
-
chat_history = await chat_utils.add_screenshot_image_block(
|
|
226
|
+
chat_history = await chat_utils.add_screenshot_image_block(
|
|
227
|
+
screenshot, chat_history
|
|
228
|
+
)
|
|
208
229
|
|
|
209
230
|
# Get and format device state using unified formatter
|
|
210
231
|
try:
|
|
@@ -213,7 +234,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
213
234
|
raw_state = self.tools.get_state()
|
|
214
235
|
|
|
215
236
|
# Format using unified function (returns 4 values)
|
|
216
|
-
formatted_text, focused_text, a11y_tree, phone_state = format_device_state(
|
|
237
|
+
formatted_text, focused_text, a11y_tree, phone_state = format_device_state(
|
|
238
|
+
raw_state
|
|
239
|
+
)
|
|
217
240
|
|
|
218
241
|
# Update shared_state if available
|
|
219
242
|
assert self.shared_state is not None, "Shared state is not set"
|
|
@@ -224,8 +247,8 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
224
247
|
|
|
225
248
|
# Extract and store package/app name (using unified update method)
|
|
226
249
|
self.shared_state.update_current_app(
|
|
227
|
-
package_name=phone_state.get(
|
|
228
|
-
activity_name=phone_state.get(
|
|
250
|
+
package_name=phone_state.get("packageName", "Unknown"),
|
|
251
|
+
activity_name=phone_state.get("currentApp", "Unknown"),
|
|
229
252
|
)
|
|
230
253
|
|
|
231
254
|
# Stream formatted state for trajectory
|
|
@@ -233,7 +256,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
233
256
|
|
|
234
257
|
# Add device state to chat using new chat_utils function
|
|
235
258
|
# This injects into LAST user message, doesn't create new message
|
|
236
|
-
chat_history = await chat_utils.add_device_state_block(
|
|
259
|
+
chat_history = await chat_utils.add_device_state_block(
|
|
260
|
+
formatted_text, chat_history
|
|
261
|
+
)
|
|
237
262
|
|
|
238
263
|
except Exception as e:
|
|
239
264
|
logger.warning(f"⚠️ Error retrieving state from the connected device: {e}")
|
|
@@ -300,7 +325,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
300
325
|
|
|
301
326
|
try:
|
|
302
327
|
self.code_exec_counter += 1
|
|
303
|
-
result = await self.executor.execute(
|
|
328
|
+
result = await self.executor.execute(
|
|
329
|
+
ExecuterState(ui_state=ctx.store.get("ui_state", None)), code
|
|
330
|
+
)
|
|
304
331
|
logger.info(f"💡 Code execution successful. Result: {result}")
|
|
305
332
|
await asyncio.sleep(self.agent_config.after_sleep_action)
|
|
306
333
|
|
|
@@ -309,8 +336,14 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
309
336
|
logger.info("✅ Task marked as complete via complete() function")
|
|
310
337
|
|
|
311
338
|
# Validate completion state
|
|
312
|
-
success =
|
|
313
|
-
|
|
339
|
+
success = (
|
|
340
|
+
self.tools.success if self.tools.success is not None else False
|
|
341
|
+
)
|
|
342
|
+
reason = (
|
|
343
|
+
self.tools.reason
|
|
344
|
+
if self.tools.reason
|
|
345
|
+
else "Task completed without reason"
|
|
346
|
+
)
|
|
314
347
|
|
|
315
348
|
# Reset finished flag for next execution
|
|
316
349
|
self.tools.finished = False
|
|
@@ -425,10 +458,9 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
425
458
|
chat_history=chat_history_str,
|
|
426
459
|
response=response_str,
|
|
427
460
|
timestamp=time.time(),
|
|
428
|
-
screenshot=(await ctx.store.get("screenshot", None))
|
|
461
|
+
screenshot=(await ctx.store.get("screenshot", None)),
|
|
429
462
|
)
|
|
430
463
|
|
|
431
|
-
|
|
432
464
|
self.episodic_memory.steps.append(step)
|
|
433
465
|
|
|
434
466
|
assert hasattr(
|
|
@@ -450,16 +482,17 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
450
482
|
time.sleep(40)
|
|
451
483
|
logger.debug("🔍 Retrying call to LLM...")
|
|
452
484
|
response = await self.llm.achat(messages=messages_to_send)
|
|
453
|
-
elif (
|
|
454
|
-
|
|
455
|
-
and "overloaded_error" in str(e)
|
|
485
|
+
elif self.llm.class_name() == "Anthropic_LLM" and "overloaded_error" in str(
|
|
486
|
+
e
|
|
456
487
|
):
|
|
457
488
|
# Use exponential backoff for Anthropic errors
|
|
458
|
-
if not hasattr(self,
|
|
489
|
+
if not hasattr(self, "_anthropic_retry_count"):
|
|
459
490
|
self._anthropic_retry_count = 0
|
|
460
491
|
self._anthropic_retry_count += 1
|
|
461
|
-
seconds = min(2
|
|
462
|
-
logger.error(
|
|
492
|
+
seconds = min(2**self._anthropic_retry_count, 60) # Cap at 60 seconds
|
|
493
|
+
logger.error(
|
|
494
|
+
f"Anthropic overload error. Retrying in {seconds} seconds... (attempt {self._anthropic_retry_count})"
|
|
495
|
+
)
|
|
463
496
|
time.sleep(seconds)
|
|
464
497
|
logger.debug("🔍 Retrying call to LLM...")
|
|
465
498
|
response = await self.llm.achat(messages=messages_to_send)
|
|
@@ -470,9 +503,7 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
470
503
|
logger.debug(" - Received response from LLM.")
|
|
471
504
|
return response
|
|
472
505
|
|
|
473
|
-
def _limit_history(
|
|
474
|
-
self, chat_history: List[ChatMessage]
|
|
475
|
-
) -> List[ChatMessage]:
|
|
506
|
+
def _limit_history(self, chat_history: List[ChatMessage]) -> List[ChatMessage]:
|
|
476
507
|
if LLM_HISTORY_LIMIT <= 0:
|
|
477
508
|
return chat_history
|
|
478
509
|
|
|
@@ -510,10 +541,15 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
510
541
|
raise Exception(f"Failed to capture final UI state: {e}") from e
|
|
511
542
|
|
|
512
543
|
# Create final observation chat history and response
|
|
513
|
-
final_chat_history = [
|
|
544
|
+
final_chat_history = [
|
|
545
|
+
{
|
|
546
|
+
"role": "system",
|
|
547
|
+
"content": "Final state observation after task completion",
|
|
548
|
+
}
|
|
549
|
+
]
|
|
514
550
|
final_response = {
|
|
515
551
|
"role": "user",
|
|
516
|
-
"content": f"Final State Observation:\nUI State: {a11y_tree}\nScreenshot: {'Available' if screenshot else 'Not available'}"
|
|
552
|
+
"content": f"Final State Observation:\nUI State: {a11y_tree}\nScreenshot: {'Available' if screenshot else 'Not available'}",
|
|
517
553
|
}
|
|
518
554
|
|
|
519
555
|
# Create final episodic memory step
|
|
@@ -521,7 +557,7 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
521
557
|
chat_history=json.dumps(final_chat_history),
|
|
522
558
|
response=json.dumps(final_response),
|
|
523
559
|
timestamp=time.time(),
|
|
524
|
-
screenshot=screenshot
|
|
560
|
+
screenshot=screenshot,
|
|
525
561
|
)
|
|
526
562
|
|
|
527
563
|
self.episodic_memory.steps.append(final_step)
|
|
@@ -11,23 +11,26 @@ class TaskInputEvent(Event):
|
|
|
11
11
|
input: list[ChatMessage]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
class TaskThinkingEvent(Event):
|
|
16
15
|
thoughts: Optional[str] = None
|
|
17
16
|
code: Optional[str] = None
|
|
18
17
|
usage: Optional[UsageResult] = None
|
|
19
18
|
|
|
19
|
+
|
|
20
20
|
class TaskExecutionEvent(Event):
|
|
21
21
|
code: str
|
|
22
22
|
globals: dict[str, str] = {}
|
|
23
23
|
locals: dict[str, str] = {}
|
|
24
24
|
|
|
25
|
+
|
|
25
26
|
class TaskExecutionResultEvent(Event):
|
|
26
27
|
output: str
|
|
27
28
|
|
|
29
|
+
|
|
28
30
|
class TaskEndEvent(Event):
|
|
29
31
|
success: bool
|
|
30
32
|
reason: str
|
|
31
33
|
|
|
34
|
+
|
|
32
35
|
class EpisodicMemoryEvent(Event):
|
|
33
36
|
episodic_memory: EpisodicMemory
|
|
@@ -6,48 +6,63 @@ from llama_index.core.workflow import Event
|
|
|
6
6
|
class ScreenshotEvent(Event):
|
|
7
7
|
screenshot: bytes
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
class MacroEvent(Event):
|
|
10
11
|
"""Base class for coordinate-based action events"""
|
|
12
|
+
|
|
11
13
|
action_type: str
|
|
12
14
|
description: str
|
|
13
15
|
|
|
16
|
+
|
|
14
17
|
class TapActionEvent(MacroEvent):
|
|
15
18
|
"""Event for tap actions with coordinates"""
|
|
19
|
+
|
|
16
20
|
x: int
|
|
17
21
|
y: int
|
|
18
22
|
element_index: int = None
|
|
19
23
|
element_text: str = ""
|
|
20
24
|
element_bounds: str = ""
|
|
21
25
|
|
|
26
|
+
|
|
22
27
|
class SwipeActionEvent(MacroEvent):
|
|
23
28
|
"""Event for swipe actions with coordinates"""
|
|
29
|
+
|
|
24
30
|
start_x: int
|
|
25
31
|
start_y: int
|
|
26
32
|
end_x: int
|
|
27
33
|
end_y: int
|
|
28
34
|
duration_ms: int
|
|
29
35
|
|
|
36
|
+
|
|
30
37
|
class DragActionEvent(MacroEvent):
|
|
31
38
|
"""Event for drag actions with coordinates"""
|
|
39
|
+
|
|
32
40
|
start_x: int
|
|
33
41
|
start_y: int
|
|
34
42
|
end_x: int
|
|
35
43
|
end_y: int
|
|
36
44
|
duration_ms: int
|
|
37
45
|
|
|
46
|
+
|
|
38
47
|
class InputTextActionEvent(MacroEvent):
|
|
39
48
|
"""Event for text input actions"""
|
|
49
|
+
|
|
40
50
|
text: str
|
|
41
51
|
|
|
52
|
+
|
|
42
53
|
class KeyPressActionEvent(MacroEvent):
|
|
43
54
|
"""Event for key press actions"""
|
|
55
|
+
|
|
44
56
|
keycode: int
|
|
45
57
|
key_name: str = ""
|
|
46
58
|
|
|
59
|
+
|
|
47
60
|
class StartAppEvent(MacroEvent):
|
|
48
|
-
""""Event for starting an app"""
|
|
61
|
+
""" "Event for starting an app"""
|
|
62
|
+
|
|
49
63
|
package: str
|
|
50
64
|
activity: str = None
|
|
51
65
|
|
|
66
|
+
|
|
52
67
|
class RecordUIStateEvent(Event):
|
|
53
68
|
ui_state: list[Dict[str, Any]]
|
|
@@ -9,9 +9,4 @@ This module contains:
|
|
|
9
9
|
from droidrun.agent.context.episodic_memory import EpisodicMemory, EpisodicMemoryStep
|
|
10
10
|
from droidrun.agent.context.task_manager import Task, TaskManager
|
|
11
11
|
|
|
12
|
-
__all__ = [
|
|
13
|
-
"EpisodicMemory",
|
|
14
|
-
"EpisodicMemoryStep",
|
|
15
|
-
"TaskManager",
|
|
16
|
-
"Task"
|
|
17
|
-
]
|
|
12
|
+
__all__ = ["EpisodicMemory", "EpisodicMemoryStep", "TaskManager", "Task"]
|
|
@@ -10,6 +10,7 @@ class Task:
|
|
|
10
10
|
"""
|
|
11
11
|
Represents a single task with its properties.
|
|
12
12
|
"""
|
|
13
|
+
|
|
13
14
|
description: str
|
|
14
15
|
status: str
|
|
15
16
|
agent_type: str
|
|
@@ -22,15 +23,13 @@ class TaskManager:
|
|
|
22
23
|
"""
|
|
23
24
|
Manages a list of tasks for an agent, each with a status and assigned specialized agent.
|
|
24
25
|
"""
|
|
26
|
+
|
|
25
27
|
STATUS_PENDING = "pending"
|
|
26
28
|
STATUS_COMPLETED = "completed"
|
|
27
29
|
STATUS_FAILED = "failed"
|
|
28
30
|
|
|
29
|
-
VALID_STATUSES = {
|
|
30
|
-
|
|
31
|
-
STATUS_COMPLETED,
|
|
32
|
-
STATUS_FAILED
|
|
33
|
-
}
|
|
31
|
+
VALID_STATUSES = {STATUS_PENDING, STATUS_COMPLETED, STATUS_FAILED}
|
|
32
|
+
|
|
34
33
|
def __init__(self):
|
|
35
34
|
"""Initializes an empty task list."""
|
|
36
35
|
self.tasks: List[Task] = []
|
|
@@ -38,7 +37,9 @@ class TaskManager:
|
|
|
38
37
|
self.message = None
|
|
39
38
|
self.task_history = []
|
|
40
39
|
# Save to working directory for user visibility
|
|
41
|
-
self.file_path = PathResolver.resolve(
|
|
40
|
+
self.file_path = PathResolver.resolve(
|
|
41
|
+
"droidrun_tasks.txt", create_if_missing=True
|
|
42
|
+
)
|
|
42
43
|
|
|
43
44
|
def get_all_tasks(self) -> List[Task]:
|
|
44
45
|
return self.tasks
|
|
@@ -66,19 +67,20 @@ class TaskManager:
|
|
|
66
67
|
self.task_history.append(task)
|
|
67
68
|
|
|
68
69
|
def get_completed_tasks(self) -> list[dict]:
|
|
69
|
-
return [
|
|
70
|
+
return [
|
|
71
|
+
task for task in self.task_history if task.status == self.STATUS_COMPLETED
|
|
72
|
+
]
|
|
70
73
|
|
|
71
74
|
def get_failed_tasks(self) -> list[dict]:
|
|
72
75
|
return [task for task in self.task_history if task.status == self.STATUS_FAILED]
|
|
73
76
|
|
|
74
|
-
|
|
75
77
|
def save_to_file(self):
|
|
76
78
|
"""Saves the current task list to a text file."""
|
|
77
79
|
try:
|
|
78
80
|
# Ensure parent directory exists
|
|
79
81
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
80
82
|
|
|
81
|
-
with open(self.file_path,
|
|
83
|
+
with open(self.file_path, "w", encoding="utf-8") as f:
|
|
82
84
|
for i, task in enumerate(self.tasks, 1):
|
|
83
85
|
f.write(f"Task {i}: {task.description}\n")
|
|
84
86
|
f.write(f"Status: {task.status}\n")
|
|
@@ -87,8 +89,6 @@ class TaskManager:
|
|
|
87
89
|
except Exception as e:
|
|
88
90
|
print(f"Error saving tasks to file: {e}")
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
92
|
def set_tasks_with_agents(self, task_assignments: List[Dict[str, str]]):
|
|
93
93
|
"""
|
|
94
94
|
Clears the current task list and sets new tasks with their assigned agents.
|
|
@@ -107,19 +107,26 @@ class TaskManager:
|
|
|
107
107
|
try:
|
|
108
108
|
self.tasks = []
|
|
109
109
|
for i, assignment in enumerate(task_assignments):
|
|
110
|
-
if not isinstance(assignment, dict) or
|
|
111
|
-
raise ValueError(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
if not isinstance(assignment, dict) or "task" not in assignment:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
f"Each task assignment must be a dictionary with 'task' key at index {i}."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
task_description = assignment["task"]
|
|
116
|
+
if (
|
|
117
|
+
not isinstance(task_description, str)
|
|
118
|
+
or not task_description.strip()
|
|
119
|
+
):
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Task description must be a non-empty string at index {i}."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
agent_type = assignment.get("agent", "Default")
|
|
118
125
|
|
|
119
126
|
task_obj = Task(
|
|
120
127
|
description=task_description.strip(),
|
|
121
128
|
status=self.STATUS_PENDING,
|
|
122
|
-
agent_type=agent_type
|
|
129
|
+
agent_type=agent_type,
|
|
123
130
|
)
|
|
124
131
|
|
|
125
132
|
self.tasks.append(task_obj)
|
|
@@ -7,7 +7,4 @@ This module provides a ReAct agent for automating Android devices using reasonin
|
|
|
7
7
|
from droidrun.agent.codeact.codeact_agent import CodeActAgent
|
|
8
8
|
from droidrun.agent.droid.droid_agent import DroidAgent
|
|
9
9
|
|
|
10
|
-
__all__ = [
|
|
11
|
-
"CodeActAgent",
|
|
12
|
-
"DroidAgent"
|
|
13
|
-
]
|
|
10
|
+
__all__ = ["CodeActAgent", "DroidAgent"]
|