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.
Files changed (133) hide show
  1. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/PKG-INFO +2 -1
  2. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/__main__.py +2 -1
  3. droidrun-0.4.0.dev1/droidrun/agent/__init__.py +6 -0
  4. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/__init__.py +1 -3
  5. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/codeact_agent.py +71 -35
  6. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/codeact/events.py +4 -1
  7. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/constants.py +1 -0
  8. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/events.py +16 -1
  9. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/__init__.py +1 -6
  10. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/task_manager.py +27 -20
  11. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/__init__.py +1 -4
  12. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/droid_agent.py +87 -80
  13. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/droid/events.py +12 -0
  14. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/__init__.py +5 -2
  15. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/events.py +2 -0
  16. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/executor/executor_agent.py +95 -50
  17. droidrun-0.4.0.dev1/droidrun/agent/executor/prompts.py +51 -0
  18. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/events.py +2 -0
  19. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/manager_agent.py +140 -100
  20. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/prompts.py +12 -5
  21. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/app_starter_workflow.py +7 -5
  22. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/text_manipulator.py +25 -6
  23. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/events.py +5 -0
  24. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/scripter_agent.py +57 -26
  25. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/usage.py +4 -0
  26. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/chat_utils.py +79 -44
  27. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/device_state_formatter.py +25 -21
  28. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/executer.py +23 -15
  29. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/inference.py +8 -9
  30. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/llm_loader.py +24 -21
  31. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/llm_picker.py +7 -3
  32. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/message_utils.py +6 -6
  33. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/tools.py +20 -15
  34. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/trajectory.py +2 -4
  35. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/composite_provider.py +8 -4
  36. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/local_provider.py +2 -3
  37. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/server_provider.py +5 -11
  38. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/logs.py +7 -2
  39. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/main.py +67 -50
  40. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/config_manager.py +50 -16
  41. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/path_resolver.py +1 -1
  42. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/prompt_loader.py +2 -2
  43. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/safe_execution.py +80 -28
  44. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/__init__.py +1 -5
  45. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/cli.py +55 -12
  46. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/portal.py +7 -1
  47. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/phoenix.py +42 -18
  48. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/tracker.py +3 -1
  49. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/adb.py +34 -15
  50. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/ios.py +6 -1
  51. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/portal_client.py +67 -46
  52. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/tools.py +23 -8
  53. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/pyproject.toml +2 -1
  54. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/uv.lock +3 -1
  55. droidrun-0.3.10.dev14/droidrun/agent/__init__.py +0 -6
  56. droidrun-0.3.10.dev14/droidrun/agent/executor/prompts.py +0 -34
  57. {droidrun-0.3.10.dev14/.github → droidrun-0.4.0.dev1/.github/workflows}/black.yml +0 -0
  58. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.github/workflows/bounty.yml +0 -0
  59. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.github/workflows/publish.yml +0 -0
  60. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.gitignore +0 -0
  61. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/.python-version +0 -0
  62. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/CHANGELOG.md +0 -0
  63. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/CONTRIBUTING.md +0 -0
  64. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/LICENSE +0 -0
  65. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/MANIFEST.in +0 -0
  66. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/README.md +0 -0
  67. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/.generated-files.txt +0 -0
  68. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/docs.json +0 -0
  69. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/favicon.png +0 -0
  70. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/logo/dark.svg +0 -0
  71. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/logo/light.svg +0 -0
  72. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/agent.mdx +0 -0
  73. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/android-control.mdx +0 -0
  74. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/concepts/portal-app.mdx +0 -0
  75. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/overview.mdx +0 -0
  76. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v1/quickstart.mdx +0 -0
  77. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/agent.mdx +0 -0
  78. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/android-control.mdx +0 -0
  79. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/planning.mdx +0 -0
  80. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/portal-app.mdx +0 -0
  81. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/concepts/tracing.mdx +0 -0
  82. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/overview.mdx +0 -0
  83. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v2/quickstart.mdx +0 -0
  84. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/agent.mdx +0 -0
  85. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/android-tools.mdx +0 -0
  86. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/models.mdx +0 -0
  87. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/concepts/portal-app.mdx +0 -0
  88. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/cli.mdx +0 -0
  89. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/gemini.mdx +0 -0
  90. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/ollama.mdx +0 -0
  91. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/openailike.mdx +0 -0
  92. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/overview.mdx +0 -0
  93. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/guides/telemetry.mdx +0 -0
  94. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/images/portal_apk.png +0 -0
  95. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/overview.mdx +0 -0
  96. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/quickstart.mdx +0 -0
  97. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/adb-tools.mdx +0 -0
  98. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/base-tools.mdx +0 -0
  99. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/droid-agent.mdx +0 -0
  100. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/docs/v3/sdk/ios-tools.mdx +0 -0
  101. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/__init__.py +0 -0
  102. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/common/__init__.py +0 -0
  103. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/context/episodic_memory.py +0 -0
  104. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/manager/__init__.py +0 -0
  105. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/oneflows/__init__.py +0 -0
  106. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/scripter/__init__.py +0 -0
  107. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/__init__.py +0 -0
  108. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/agent/utils/async_utils.py +0 -0
  109. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/__init__.py +0 -0
  110. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/app_card_provider.py +0 -0
  111. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/app_cards/providers/__init__.py +0 -0
  112. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/cli/__init__.py +0 -0
  113. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/README.md +0 -0
  114. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/app_cards.json +0 -0
  115. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/app_cards/gmail.md +0 -0
  116. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/codeact/system.jinja2 +0 -0
  117. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/codeact/user.jinja2 +0 -0
  118. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/executor/rev1.jinja2 +0 -0
  119. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/executor/system.jinja2 +0 -0
  120. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/manager/rev1.jinja2 +0 -0
  121. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/manager/system.jinja2 +0 -0
  122. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config/prompts/scripter/system.jinja2 +0 -0
  123. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_example.yaml +0 -0
  124. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/config_manager/__init__.py +0 -0
  125. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/__main__.py +0 -0
  126. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/macro/replay.py +0 -0
  127. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/__init__.py +0 -0
  128. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/telemetry/events.py +0 -0
  129. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/droidrun/tools/__init__.py +0 -0
  130. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/gen-docs-sdk-ref.sh +0 -0
  131. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/setup.py +0 -0
  132. {droidrun-0.3.10.dev14 → droidrun-0.4.0.dev1}/static/droidrun-dark.png +0 -0
  133. {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.10.dev14
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
@@ -1,7 +1,8 @@
1
1
  """
2
2
  DroidRun main entry point
3
3
  """
4
+
4
5
  from droidrun.cli.main import cli
5
6
 
6
- if __name__ == '__main__':
7
+ if __name__ == "__main__":
7
8
  cli()
@@ -0,0 +1,6 @@
1
+ # import logging
2
+
3
+ # logger = logging.getLogger("droidrun")
4
+ # logger.propagate = False # Don't send to root logger
5
+ # logger.handlers = [] # No handlers by default
6
+ # logger.setLevel(logging.INFO) # Or WARNING
@@ -1,5 +1,3 @@
1
1
  from droidrun.agent.codeact.codeact_agent import CodeActAgent
2
2
 
3
- __all__ = [
4
- "CodeActAgent"
5
- ]
3
+ __all__ = ["CodeActAgent"]
@@ -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] = lambda *args, f=func, ti=tools_instance, **kwargs: f(ti, *args, **kwargs)
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 += "\n- remember(information: str): Remember information for later use"
107
- self.tool_descriptions += "\n- complete(success: bool, reason: str): Mark task as complete"
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=safe_config.get_allowed_modules() if safe_config and safe_mode else None,
127
- blocked_modules=safe_config.get_blocked_modules() if safe_config and safe_mode else None,
128
- allowed_builtins=safe_config.get_allowed_builtins() if safe_config and safe_mode else None,
129
- blocked_builtins=safe_config.get_blocked_builtins() if safe_config and safe_mode else None,
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(self.remembered_info, chat_history)
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(screenshot, chat_history)
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(raw_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('packageName', 'Unknown'),
228
- activity_name=phone_state.get('currentApp', 'Unknown')
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(formatted_text, chat_history)
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(ExecuterState(ui_state=ctx.store.get("ui_state", None)), code)
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 = self.tools.success if self.tools.success is not None else False
313
- reason = self.tools.reason if self.tools.reason else "Task completed without reason"
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
- self.llm.class_name() == "Anthropic_LLM"
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, '_anthropic_retry_count'):
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 ** self._anthropic_retry_count, 60) # Cap at 60 seconds
462
- logger.error(f"Anthropic overload error. Retrying in {seconds} seconds... (attempt {self._anthropic_retry_count})")
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 = [{"role": "system", "content": "Final state observation after task completion"}]
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
@@ -1,2 +1,3 @@
1
1
  """Max number of recent conversation steps to include in LLM prompt"""
2
+
2
3
  LLM_HISTORY_LIMIT = 20
@@ -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
- STATUS_PENDING,
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("droidrun_tasks.txt", create_if_missing=True)
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 [task for task in self.task_history if task.status == self.STATUS_COMPLETED]
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, 'w', encoding='utf-8') as f:
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 'task' not in assignment:
111
- raise ValueError(f"Each task assignment must be a dictionary with 'task' key at index {i}.")
112
-
113
- task_description = assignment['task']
114
- if not isinstance(task_description, str) or not task_description.strip():
115
- raise ValueError(f"Task description must be a non-empty string at index {i}.")
116
-
117
- agent_type = assignment.get('agent', 'Default')
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"]