droidrun 0.1.0__py3-none-any.whl → 0.3.0__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/__init__.py +22 -10
- droidrun/__main__.py +1 -2
- droidrun/adb/__init__.py +3 -3
- droidrun/adb/device.py +2 -2
- droidrun/adb/manager.py +2 -2
- droidrun/agent/__init__.py +5 -15
- droidrun/agent/codeact/__init__.py +11 -0
- droidrun/agent/codeact/codeact_agent.py +420 -0
- droidrun/agent/codeact/events.py +28 -0
- droidrun/agent/codeact/prompts.py +26 -0
- droidrun/agent/common/default.py +5 -0
- droidrun/agent/common/events.py +4 -0
- droidrun/agent/context/__init__.py +23 -0
- droidrun/agent/context/agent_persona.py +15 -0
- droidrun/agent/context/context_injection_manager.py +66 -0
- droidrun/agent/context/episodic_memory.py +15 -0
- droidrun/agent/context/personas/__init__.py +11 -0
- droidrun/agent/context/personas/app_starter.py +44 -0
- droidrun/agent/context/personas/default.py +95 -0
- droidrun/agent/context/personas/extractor.py +52 -0
- droidrun/agent/context/personas/ui_expert.py +107 -0
- droidrun/agent/context/reflection.py +20 -0
- droidrun/agent/context/task_manager.py +124 -0
- droidrun/agent/context/todo.txt +4 -0
- droidrun/agent/droid/__init__.py +13 -0
- droidrun/agent/droid/droid_agent.py +357 -0
- droidrun/agent/droid/events.py +28 -0
- droidrun/agent/oneflows/reflector.py +265 -0
- droidrun/agent/planner/__init__.py +13 -0
- droidrun/agent/planner/events.py +16 -0
- droidrun/agent/planner/planner_agent.py +268 -0
- droidrun/agent/planner/prompts.py +124 -0
- droidrun/agent/utils/__init__.py +3 -0
- droidrun/agent/utils/async_utils.py +17 -0
- droidrun/agent/utils/chat_utils.py +312 -0
- droidrun/agent/utils/executer.py +132 -0
- droidrun/agent/utils/llm_picker.py +147 -0
- droidrun/agent/utils/trajectory.py +184 -0
- droidrun/cli/__init__.py +1 -1
- droidrun/cli/logs.py +283 -0
- droidrun/cli/main.py +358 -149
- droidrun/run.py +105 -0
- droidrun/tools/__init__.py +4 -30
- droidrun/tools/adb.py +879 -0
- droidrun/tools/ios.py +594 -0
- droidrun/tools/tools.py +99 -0
- droidrun-0.3.0.dist-info/METADATA +149 -0
- droidrun-0.3.0.dist-info/RECORD +52 -0
- droidrun/agent/llm_reasoning.py +0 -567
- droidrun/agent/react_agent.py +0 -556
- droidrun/llm/__init__.py +0 -24
- droidrun/tools/actions.py +0 -854
- droidrun/tools/device.py +0 -29
- droidrun-0.1.0.dist-info/METADATA +0 -276
- droidrun-0.1.0.dist-info/RECORD +0 -20
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/WHEEL +0 -0
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/entry_points.txt +0 -0
- {droidrun-0.1.0.dist-info → droidrun-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
"""
|
2
|
+
Context Injection Manager - Manages specialized agent personas with dynamic tool and context injection.
|
3
|
+
|
4
|
+
This module provides the ContextInjectionManager class that manages different agent personas,
|
5
|
+
each with specific system prompts, contexts, and tool subsets tailored for specialized tasks.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
from typing import Optional, List
|
10
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
11
|
+
#import chromadb
|
12
|
+
import json
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
logger = logging.getLogger("droidrun")
|
16
|
+
|
17
|
+
class ContextInjectionManager:
|
18
|
+
"""
|
19
|
+
Manages different agent personas with specialized contexts and tool subsets.
|
20
|
+
|
21
|
+
This class is responsible for:
|
22
|
+
- Defining agent personas with specific capabilities
|
23
|
+
- Injecting appropriate system prompts based on agent type
|
24
|
+
- Filtering tool lists to match agent specialization
|
25
|
+
- Providing context-aware configurations for CodeActAgent instances
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
personas: List[AgentPersona]
|
31
|
+
):
|
32
|
+
"""Initialize the Context Injection Manager with predefined personas."""
|
33
|
+
|
34
|
+
self.personas = {}
|
35
|
+
for persona in personas:
|
36
|
+
self.personas[persona.name] = persona
|
37
|
+
|
38
|
+
|
39
|
+
def _load_persona(self, data: str) -> AgentPersona:
|
40
|
+
persona = json.loads(data)
|
41
|
+
logger.info(f"🎭 Loaded persona: {persona['name']}")
|
42
|
+
return AgentPersona(
|
43
|
+
name=persona['name'],
|
44
|
+
system_prompt=persona['system_prompt'],
|
45
|
+
allowed_tools=persona['allowed_tools'],
|
46
|
+
description=persona['description'],
|
47
|
+
expertise_areas=persona['expertise_areas'],
|
48
|
+
user_prompt=persona['user_prompt'],
|
49
|
+
required_context=persona['required_context'],
|
50
|
+
)
|
51
|
+
|
52
|
+
def get_persona(self, agent_type: str) -> Optional[AgentPersona]:
|
53
|
+
"""
|
54
|
+
Get a specific agent persona by type.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
agent_type: The type of agent ("UIExpert" or "AppStarterExpert")
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
AgentPersona instance or None if not found
|
61
|
+
"""
|
62
|
+
|
63
|
+
return self.personas.get(agent_type)
|
64
|
+
|
65
|
+
def get_all_personas(self) -> List[str]:
|
66
|
+
return self.personas
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
@dataclass
|
6
|
+
class EpisodicMemoryStep:
|
7
|
+
chat_history: str
|
8
|
+
response: str
|
9
|
+
timestamp: float
|
10
|
+
screenshot: Optional[bytes]
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class EpisodicMemory:
|
14
|
+
persona: AgentPersona
|
15
|
+
steps: List[EpisodicMemoryStep] = field(default_factory=list)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
2
|
+
from droidrun.tools import Tools
|
3
|
+
|
4
|
+
APP_STARTER_EXPERT = AgentPersona(
|
5
|
+
name="AppStarterExpert",
|
6
|
+
description="Specialized in app launching",
|
7
|
+
expertise_areas=[
|
8
|
+
"app launching"
|
9
|
+
],
|
10
|
+
allowed_tools=[
|
11
|
+
Tools.start_app.__name__,
|
12
|
+
Tools.complete.__name__
|
13
|
+
],
|
14
|
+
required_context=[
|
15
|
+
"packages"
|
16
|
+
],
|
17
|
+
user_prompt="""
|
18
|
+
**Current Request:**
|
19
|
+
{goal}
|
20
|
+
**Is the precondition met? What is your reasoning and the next step to address this request?** Explain your thought process then provide code in ```python ... ``` tags if needed.""""",
|
21
|
+
|
22
|
+
system_prompt= """You are an App Starter Expert specialized in Android application lifecycle management. Your core expertise includes:
|
23
|
+
|
24
|
+
**Primary Capabilities:**
|
25
|
+
- Launch Android applications by package name
|
26
|
+
- Use proper package name format (com.example.app)
|
27
|
+
|
28
|
+
## Response Format:
|
29
|
+
Example of proper code format:
|
30
|
+
To launch the Calculator app, I need to use the start_app function with the correct package name.
|
31
|
+
```python
|
32
|
+
# Launch the Calculator app
|
33
|
+
start_app("com.android.calculator2")
|
34
|
+
complete(success=True)
|
35
|
+
```
|
36
|
+
|
37
|
+
In addition to the Python Standard Library and any functions you have already written, you can use the following functions:
|
38
|
+
{tool_descriptions}
|
39
|
+
|
40
|
+
Reminder: Always place your Python code between ```...``` tags when you want to run code.
|
41
|
+
|
42
|
+
You focus ONLY on app launching and package management - UI interactions within apps are handled by UI specialists.""",
|
43
|
+
|
44
|
+
)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
2
|
+
from droidrun.tools import Tools
|
3
|
+
|
4
|
+
DEFAULT = AgentPersona(
|
5
|
+
name="Default",
|
6
|
+
description="Default Agent. Use this as your Default",
|
7
|
+
expertise_areas=[
|
8
|
+
"UI navigation", "button interactions", "text input",
|
9
|
+
"menu navigation", "form filling", "scrolling", "app launching"
|
10
|
+
],
|
11
|
+
allowed_tools=[
|
12
|
+
Tools.swipe.__name__,
|
13
|
+
Tools.input_text.__name__,
|
14
|
+
Tools.press_key.__name__,
|
15
|
+
Tools.tap_by_index.__name__,
|
16
|
+
Tools.start_app.__name__,
|
17
|
+
Tools.list_packages.__name__,
|
18
|
+
Tools.remember.__name__,
|
19
|
+
Tools.complete.__name__
|
20
|
+
],
|
21
|
+
required_context=[
|
22
|
+
"ui_state",
|
23
|
+
"screenshot",
|
24
|
+
"phone_state"
|
25
|
+
],
|
26
|
+
user_prompt="""
|
27
|
+
**Current Request:**
|
28
|
+
{goal}
|
29
|
+
**Is the precondition met? What is your reasoning and the next step to address this request?**
|
30
|
+
Explain your thought process then provide code in ```python ... ``` tags if needed.
|
31
|
+
""""",
|
32
|
+
|
33
|
+
system_prompt="""
|
34
|
+
You are a helpful AI assistant that can write and execute Python code to solve problems.
|
35
|
+
|
36
|
+
You will be given a task to perform. You should output:
|
37
|
+
- Python code wrapped in ``` tags that provides the solution to the task, or a step towards the solution.
|
38
|
+
- If there is a precondition for the task, you MUST check if it is met.
|
39
|
+
- If a goal's precondition is unmet, fail the task by calling `complete(success=False, reason='...')` with an explanation.
|
40
|
+
- If you task is complete, you should use the complete(success:bool, reason:str) function within a code block to mark it as finished. The success parameter should be True if the task was completed successfully, and False otherwise. The reason parameter should be a string explaining the reason for failure if failed.
|
41
|
+
|
42
|
+
|
43
|
+
## Context:
|
44
|
+
The following context is given to you for analysis:
|
45
|
+
- **ui_state**: A list of all currently visible UI elements with their indices. Use this to understand what interactive elements are available on the screen.
|
46
|
+
- **screenshots**: A visual screenshot of the current state of the Android screen. This provides visual context for what the user sees. screenshots won't be saved in the chat history. So, make sure to describe what you see and explain the key parts of your plan in your thoughts, as those will be saved and used to assist you in future steps.
|
47
|
+
- **phone_state**: The current app you are navigating in. This tells you which application context you're working within.
|
48
|
+
- **chat history**: You are also given the history of your actions (if any) from your previous steps.
|
49
|
+
NOTE: you don't have access to these inputs in your tool calling context
|
50
|
+
|
51
|
+
## Response Format:
|
52
|
+
Example of proper code format:
|
53
|
+
**Task Assignment:**
|
54
|
+
**Task:** "Precondition: Settings app is open. Goal: Navigate to Wi-Fi settings and connect to the network 'HomeNetwork'."
|
55
|
+
|
56
|
+
**(Step 1) Agent Analysis:** I can see the Settings app is open from the screenshot. This is a multi-step task that requires me to first navigate to Wi-Fi settings, then ensure Wi-Fi is enabled, and finally connect to 'HomeNetwork'. Let me start by finding and tapping on the Wi-Fi option in the settings menu. Looking at the UI elements, I can see "Wi-Fi" option at index 3.
|
57
|
+
|
58
|
+
**(Step 1) Agent Action:**
|
59
|
+
```python
|
60
|
+
# First step: Navigate to Wi-Fi settings
|
61
|
+
tap_by_index(3)
|
62
|
+
```
|
63
|
+
|
64
|
+
**(Step 2) Agent Analysis:** Good! I've successfully navigated to the Wi-Fi settings screen. Now I can see the Wi-Fi settings interface. I notice that Wi-Fi appears to be turned off based on the toggle switch at index 1. I need to turn it on before I can see available networks and connect to 'HomeNetwork'.
|
65
|
+
|
66
|
+
**(Step 2) Agent Action:**
|
67
|
+
```python
|
68
|
+
# Second step: Turn on Wi-Fi to see available networks
|
69
|
+
tap_by_index(1)
|
70
|
+
```
|
71
|
+
|
72
|
+
**(Step 3) Agent Analysis:** Excellent! Wi-Fi is now enabled and I can see a list of available networks appearing on the screen. I can see 'HomeNetwork' in the list at index 5. This is the final step - I need to tap on it to initiate the connection, which will complete my assigned task.
|
73
|
+
|
74
|
+
**(Step 3) Agent Action:**
|
75
|
+
```python
|
76
|
+
# Final step: Connect to the target network
|
77
|
+
tap_by_index(5)
|
78
|
+
complete(success=True, reason="Successfully navigated to Wi-Fi settings and initiated connection to HomeNetwork")
|
79
|
+
```
|
80
|
+
```
|
81
|
+
|
82
|
+
## Tools:
|
83
|
+
In addition to the Python Standard Library and any functions you have already written, you can use the following functions:
|
84
|
+
{tool_descriptions}
|
85
|
+
|
86
|
+
|
87
|
+
## Final Answer Guidelines:
|
88
|
+
- When providing a final answer, focus on directly answering the user's question in the response format given
|
89
|
+
- Present the results clearly and concisely as if you computed them directly
|
90
|
+
- Structure your response like you're directly answering the user's query, not explaining how you solved it
|
91
|
+
|
92
|
+
Reminder: Always place your Python code between ```...``` tags when you want to run code.
|
93
|
+
"""
|
94
|
+
|
95
|
+
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
2
|
+
from droidrun.tools import Tools
|
3
|
+
|
4
|
+
EXTRACTOR = AgentPersona(
|
5
|
+
name="DataExtractor",
|
6
|
+
description="Specialized in extracting data from UI elements and screenshots",
|
7
|
+
expertise_areas=[
|
8
|
+
"data extraction",
|
9
|
+
"UI analysis",
|
10
|
+
"text recognition"
|
11
|
+
],
|
12
|
+
allowed_tools=[
|
13
|
+
Tools.extract.__name__,
|
14
|
+
Tools.complete.__name__
|
15
|
+
],
|
16
|
+
required_context=[
|
17
|
+
"ui_state",
|
18
|
+
"screenshot"
|
19
|
+
],
|
20
|
+
user_prompt="""
|
21
|
+
**Current Request:**
|
22
|
+
{goal}
|
23
|
+
**What data needs to be extracted?
|
24
|
+
Analyze the current UI state and screenshot, then extract the requested information.
|
25
|
+
** Explain your thought process then provide code in ```python ... ``` tags if needed.""",
|
26
|
+
|
27
|
+
system_prompt= """
|
28
|
+
You are a Data Extractor Expert specialized in analyzing Android UI states and screenshots to extract specific information. Your core expertise includes:
|
29
|
+
|
30
|
+
**Primary Capabilities:**
|
31
|
+
- Analyze UI elements from ui_state data
|
32
|
+
- Extract text, values, and structured data from screenshots
|
33
|
+
- Identify and parse specific UI components (buttons, text fields, lists, etc.)
|
34
|
+
- Extract data based on user requirements
|
35
|
+
|
36
|
+
## Response Format:
|
37
|
+
Example of proper code format:
|
38
|
+
To extract the current battery percentage from the status bar:
|
39
|
+
```python
|
40
|
+
# Extract battery percentage from UI state
|
41
|
+
battery_data = extract("battery percentage")
|
42
|
+
complete(success=True)
|
43
|
+
```
|
44
|
+
|
45
|
+
In addition to the Python Standard Library and any functions you have already written, you can use the following functions:
|
46
|
+
{tool_descriptions}
|
47
|
+
|
48
|
+
Reminder: Always place your Python code between ```...``` tags when you want to run code.
|
49
|
+
|
50
|
+
You focus ONLY on data extraction from the current UI state and screenshot - navigation and UI interactions are handled by other specialists.""",
|
51
|
+
|
52
|
+
)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
from droidrun.agent.context.agent_persona import AgentPersona
|
2
|
+
from droidrun.tools import Tools
|
3
|
+
|
4
|
+
UI_EXPERT = AgentPersona(
|
5
|
+
name="UIExpert",
|
6
|
+
description="Specialized in UI interactions, navigation, and form filling",
|
7
|
+
expertise_areas=[
|
8
|
+
"UI navigation", "button interactions", "text input",
|
9
|
+
"menu navigation", "form filling", "scrolling"
|
10
|
+
],
|
11
|
+
allowed_tools=[
|
12
|
+
Tools.swipe.__name__,
|
13
|
+
Tools.input_text.__name__,
|
14
|
+
Tools.press_key.__name__,
|
15
|
+
Tools.tap_by_index.__name__,
|
16
|
+
Tools.remember.__name__,
|
17
|
+
Tools.complete.__name__
|
18
|
+
],
|
19
|
+
required_context=[
|
20
|
+
"ui_state",
|
21
|
+
"screenshot",
|
22
|
+
"phone_state",
|
23
|
+
"memory"
|
24
|
+
],
|
25
|
+
user_prompt="""
|
26
|
+
**Current Request:**
|
27
|
+
{goal}
|
28
|
+
**Is the precondition met? What is your reasoning and the next step to address this request?** Explain your thought process then provide code in ```python ... ``` tags if needed.""""",
|
29
|
+
|
30
|
+
|
31
|
+
system_prompt="""You are a UI Expert specialized in Android interface interactions. Your core expertise includes:
|
32
|
+
|
33
|
+
**Primary Capabilities:**
|
34
|
+
- Navigate through Android UI elements with precision
|
35
|
+
- Interact with buttons, menus, forms, and interactive elements
|
36
|
+
- Enter text into input fields and search bars
|
37
|
+
- Scroll through content and lists
|
38
|
+
- Handle complex UI navigation workflows
|
39
|
+
- Recognize and interact with various UI patterns (tabs, drawers, dialogs, etc.)
|
40
|
+
|
41
|
+
**Your Approach:**
|
42
|
+
- Focus on understanding the current UI state through screenshots and element data
|
43
|
+
- Use precise element identification for reliable interactions
|
44
|
+
- Handle dynamic UI changes and loading states gracefully
|
45
|
+
- Provide clear feedback on UI interactions and their outcomes
|
46
|
+
- Adapt to different app interfaces and UI patterns
|
47
|
+
|
48
|
+
**Key Principles:**
|
49
|
+
- Always analyze the current screen state before taking action
|
50
|
+
- Prefer using element indices for reliable targeting
|
51
|
+
- Provide descriptive feedback about what you're interacting with
|
52
|
+
- Handle edge cases like loading screens, popups, and navigation changes
|
53
|
+
- Remember important UI state information for context
|
54
|
+
|
55
|
+
You do NOT handle app launching or package management - that's handled by other specialists.
|
56
|
+
|
57
|
+
|
58
|
+
## Available Context:
|
59
|
+
In your execution environment, you have access to:
|
60
|
+
- `ui_elements`: A global variable containing the current UI elements from the device. This is automatically updated before each code execution and contains the latest UI elements that were fetched.
|
61
|
+
|
62
|
+
## Response Format:
|
63
|
+
Example of proper code format:
|
64
|
+
To calculate the area of a circle, I need to use the formula: area = pi * radius^2. I will write a function to do this.
|
65
|
+
```python
|
66
|
+
import math
|
67
|
+
|
68
|
+
def calculate_area(radius):
|
69
|
+
return math.pi * radius**2
|
70
|
+
|
71
|
+
# Calculate the area for radius = 5
|
72
|
+
area = calculate_area(5)
|
73
|
+
print(f"The area of the circle is {{area:.2f}} square units")
|
74
|
+
```
|
75
|
+
|
76
|
+
Another example (with for loop):
|
77
|
+
To calculate the sum of numbers from 1 to 10, I will use a for loop.
|
78
|
+
```python
|
79
|
+
sum = 0
|
80
|
+
for i in range(1, 11):
|
81
|
+
sum += i
|
82
|
+
print(f"The sum of numbers from 1 to 10 is {{sum}}")
|
83
|
+
```
|
84
|
+
|
85
|
+
In addition to the Python Standard Library and any functions you have already written, you can use the following functions:
|
86
|
+
{tool_descriptions}
|
87
|
+
|
88
|
+
You'll receive a screenshot showing the current screen and its UI elements to help you complete the task. However, screenshots won't be saved in the chat history. So, make sure to describe what you see and explain the key parts of your plan in your thoughts, as those will be saved and used to assist you in future steps.
|
89
|
+
|
90
|
+
**Important Notes:**
|
91
|
+
- If there is a precondition for the task, you MUST check if it is met.
|
92
|
+
- If a goal's precondition is unmet, fail the task by calling `complete(success=False, reason='...')` with an explanation.
|
93
|
+
|
94
|
+
## Final Answer Guidelines:
|
95
|
+
- When providing a final answer, focus on directly answering the user's question
|
96
|
+
- Avoid referencing the code you generated unless specifically asked
|
97
|
+
- Present the results clearly and concisely as if you computed them directly
|
98
|
+
- If relevant, you can briefly mention general methods used, but don't include code snippets in the final answer
|
99
|
+
- Structure your response like you're directly answering the user's query, not explaining how you solved it
|
100
|
+
|
101
|
+
Reminder: Always place your Python code between ```...``` tags when you want to run code.
|
102
|
+
|
103
|
+
You MUST ALWAYS to include your reasoning and thought process outside of the code block. You MUST DOUBLE CHECK that TASK IS COMPLETE with a SCREENSHOT.
|
104
|
+
"""
|
105
|
+
)
|
106
|
+
|
107
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
@dataclass
|
5
|
+
class Reflection:
|
6
|
+
"""Represents the result of a reflection analysis on episodic memory."""
|
7
|
+
goal_achieved: bool
|
8
|
+
summary: str
|
9
|
+
advice: Optional[str] = None
|
10
|
+
raw_response: Optional[str] = None
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def from_dict(cls, data: dict) -> 'Reflection':
|
14
|
+
"""Create a Reflection from a dictionary (e.g., parsed JSON)."""
|
15
|
+
return cls(
|
16
|
+
goal_achieved=data.get('goal_achieved', False),
|
17
|
+
summary=data.get('summary', ''),
|
18
|
+
advice=data.get('advice'),
|
19
|
+
raw_response=data.get('raw_response')
|
20
|
+
)
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import os
|
2
|
+
from typing import List, Dict
|
3
|
+
from dataclasses import dataclass
|
4
|
+
import copy
|
5
|
+
|
6
|
+
@dataclass
|
7
|
+
class Task:
|
8
|
+
"""
|
9
|
+
Represents a single task with its properties.
|
10
|
+
"""
|
11
|
+
description: str
|
12
|
+
status: str
|
13
|
+
agent_type: str
|
14
|
+
|
15
|
+
|
16
|
+
class TaskManager:
|
17
|
+
"""
|
18
|
+
Manages a list of tasks for an agent, each with a status and assigned specialized agent.
|
19
|
+
"""
|
20
|
+
STATUS_PENDING = "pending"
|
21
|
+
STATUS_COMPLETED = "completed"
|
22
|
+
STATUS_FAILED = "failed"
|
23
|
+
|
24
|
+
VALID_STATUSES = {
|
25
|
+
STATUS_PENDING,
|
26
|
+
STATUS_COMPLETED,
|
27
|
+
STATUS_FAILED
|
28
|
+
}
|
29
|
+
def __init__(self):
|
30
|
+
"""Initializes an empty task list."""
|
31
|
+
self.tasks: List[Task] = []
|
32
|
+
self.goal_completed = False
|
33
|
+
self.message = None
|
34
|
+
self.task_history = []
|
35
|
+
self.file_path = os.path.join(os.path.dirname(__file__), "todo.txt")
|
36
|
+
|
37
|
+
def get_all_tasks(self) -> List[Task]:
|
38
|
+
return self.tasks
|
39
|
+
|
40
|
+
def get_task_history(self):
|
41
|
+
return self.task_history
|
42
|
+
|
43
|
+
def complete_task(self, task: Task):
|
44
|
+
task = copy.deepcopy(task)
|
45
|
+
task.status = self.STATUS_COMPLETED
|
46
|
+
self.task_history.append(task)
|
47
|
+
|
48
|
+
def fail_task(self, task: Task):
|
49
|
+
task = copy.deepcopy(task)
|
50
|
+
task.status = self.STATUS_FAILED
|
51
|
+
self.task_history.append(task)
|
52
|
+
|
53
|
+
def get_completed_tasks(self) -> list[dict]:
|
54
|
+
return [task for task in self.task_history if task.status == self.STATUS_COMPLETED]
|
55
|
+
|
56
|
+
def get_failed_tasks(self) -> list[dict]:
|
57
|
+
return [task for task in self.task_history if task.status == self.STATUS_FAILED]
|
58
|
+
|
59
|
+
|
60
|
+
def save_to_file(self):
|
61
|
+
"""Saves the current task list to a Markdown file."""
|
62
|
+
try:
|
63
|
+
with open(self.file_path, 'w', encoding='utf-8') as f:
|
64
|
+
for i, task in enumerate(self.tasks, 1):
|
65
|
+
f.write(f"Task {i}: {task.description}\n")
|
66
|
+
f.write(f"Status: {task.status}\n")
|
67
|
+
f.write(f"Agent: {task.agent_type}\n")
|
68
|
+
f.write("-" * 40 + "\n")
|
69
|
+
except Exception as e:
|
70
|
+
print(f"Error saving tasks to file: {e}")
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
def set_tasks_with_agents(self, task_assignments: List[Dict[str, str]]):
|
75
|
+
"""
|
76
|
+
Clears the current task list and sets new tasks with their assigned agents.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
task_assignments: A list of dictionaries, each containing:
|
80
|
+
- 'task': The task description string
|
81
|
+
- 'agent': The agent type
|
82
|
+
|
83
|
+
Example:
|
84
|
+
task_manager.set_tasks_with_agents([
|
85
|
+
{'task': 'Open Gmail app', 'agent': 'AppStarterExpert'},
|
86
|
+
{'task': 'Navigate to compose email', 'agent': 'UIExpert'}
|
87
|
+
])
|
88
|
+
"""
|
89
|
+
try:
|
90
|
+
self.tasks = []
|
91
|
+
for i, assignment in enumerate(task_assignments):
|
92
|
+
if not isinstance(assignment, dict) or 'task' not in assignment:
|
93
|
+
raise ValueError(f"Each task assignment must be a dictionary with 'task' key at index {i}.")
|
94
|
+
|
95
|
+
task_description = assignment['task']
|
96
|
+
if not isinstance(task_description, str) or not task_description.strip():
|
97
|
+
raise ValueError(f"Task description must be a non-empty string at index {i}.")
|
98
|
+
|
99
|
+
agent_type = assignment.get('agent', 'Default')
|
100
|
+
|
101
|
+
task_obj = Task(
|
102
|
+
description=task_description.strip(),
|
103
|
+
status=self.STATUS_PENDING,
|
104
|
+
agent_type=agent_type
|
105
|
+
)
|
106
|
+
|
107
|
+
self.tasks.append(task_obj)
|
108
|
+
|
109
|
+
print(f"Tasks set with agents: {len(self.tasks)} tasks added.")
|
110
|
+
self.save_to_file()
|
111
|
+
except Exception as e:
|
112
|
+
print(f"Error setting tasks with agents: {e}")
|
113
|
+
|
114
|
+
def complete_goal(self, message: str):
|
115
|
+
"""
|
116
|
+
Marks the goal as completed, use this whether the task completion was successful or on failure.
|
117
|
+
This method should be called when the task is finished, regardless of the outcome.
|
118
|
+
|
119
|
+
Args:
|
120
|
+
message: The message to be logged.
|
121
|
+
"""
|
122
|
+
self.goal_completed = True
|
123
|
+
self.message = message
|
124
|
+
print(f"Goal completed: {message}")
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
Droidrun Agent Module.
|
3
|
+
|
4
|
+
This module provides a ReAct agent for automating Android devices using reasoning and acting.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from droidrun.agent.codeact.codeact_agent import CodeActAgent
|
8
|
+
from droidrun.agent.droid.droid_agent import DroidAgent
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"CodeActAgent",
|
12
|
+
"DroidAgent"
|
13
|
+
]
|