1
- from typing import Optional
1
+ from enum import Enum
2
+ from typing import Annotated , Literal , Optional , Union
2
3
from uuid import uuid4
3
4
4
5
from pydantic import BaseModel , Field
5
6
6
7
from core .agents .base import BaseAgent
7
8
from core .agents .convo import AgentConvo
8
- from core .agents .mixins import TaskSteps
9
+ from core .agents .mixins import RelevantFilesMixin
9
10
from core .agents .response import AgentResponse , ResponseType
10
11
from core .config import TASK_BREAKDOWN_AGENT_NAME
11
12
from core .db .models .project_state import IterationStatus , TaskStatus
17
18
log = get_logger (__name__ )
18
19
19
20
20
- class RelevantFiles (BaseModel ):
21
- relevant_files : list [str ] = Field (description = "List of relevant files for the current task." )
21
+ class StepType (str , Enum ):
22
+ COMMAND = "command"
23
+ SAVE_FILE = "save_file"
24
+ HUMAN_INTERVENTION = "human_intervention"
22
25
23
26
24
- class Developer (BaseAgent ):
27
+ class CommandOptions (BaseModel ):
28
+ command : str = Field (description = "Command to run" )
29
+ timeout : int = Field (description = "Timeout in seconds" )
30
+ success_message : str = ""
31
+
32
+
33
+ class SaveFileOptions (BaseModel ):
34
+ path : str
35
+
36
+
37
+ class SaveFileStep (BaseModel ):
38
+ type : Literal [StepType .SAVE_FILE ] = StepType .SAVE_FILE
39
+ save_file : SaveFileOptions
40
+
41
+
42
+ class CommandStep (BaseModel ):
43
+ type : Literal [StepType .COMMAND ] = StepType .COMMAND
44
+ command : CommandOptions
45
+
46
+
47
+ class HumanInterventionStep (BaseModel ):
48
+ type : Literal [StepType .HUMAN_INTERVENTION ] = StepType .HUMAN_INTERVENTION
49
+ human_intervention_description : str
50
+
51
+
52
+ Step = Annotated [
53
+ Union [SaveFileStep , CommandStep , HumanInterventionStep ],
54
+ Field (discriminator = "type" ),
55
+ ]
56
+
57
+
58
+ class TaskSteps (BaseModel ):
59
+ steps : list [Step ]
60
+
61
+
62
+ class Developer (RelevantFilesMixin , BaseAgent ):
25
63
agent_type = "developer"
26
64
display_name = "Developer"
27
65
@@ -96,7 +134,8 @@ async def breakdown_current_iteration(self, task_review_feedback: Optional[str]
96
134
log .debug (f"Breaking down the iteration { description } " )
97
135
await self .send_message ("Breaking down the current task iteration ..." )
98
136
99
- await self .get_relevant_files (user_feedback , description )
137
+ if self .current_state .files and self .current_state .relevant_files is None :
138
+ return await self .get_relevant_files (user_feedback , description )
100
139
101
140
await self .ui .send_task_progress (
102
141
n_tasks , # iterations and reviews can be created only one at a time, so we are always on last one
@@ -114,7 +153,6 @@ async def breakdown_current_iteration(self, task_review_feedback: Optional[str]
114
153
AgentConvo (self )
115
154
.template (
116
155
"iteration" ,
117
- current_task = current_task ,
118
156
user_feedback = user_feedback ,
119
157
user_feedback_qa = None ,
120
158
next_solution_to_try = None ,
@@ -175,7 +213,7 @@ async def breakdown_current_task(self) -> AgentResponse:
175
213
log .debug (f"Current state files: { len (self .current_state .files )} , relevant { self .current_state .relevant_files } " )
176
214
# Check which files are relevant to the current task
177
215
if self .current_state .files and self .current_state .relevant_files is None :
178
- await self .get_relevant_files ()
216
+ return await self .get_relevant_files ()
179
217
180
218
current_task_index = self .current_state .tasks .index (current_task )
181
219
@@ -189,6 +227,8 @@ async def breakdown_current_task(self) -> AgentResponse:
189
227
)
190
228
response : str = await llm (convo )
191
229
230
+ await self .get_relevant_files (None , response )
231
+
192
232
self .next_state .tasks [current_task_index ] = {
193
233
** current_task ,
194
234
"instructions" : response ,
@@ -214,31 +254,6 @@ async def breakdown_current_task(self) -> AgentResponse:
214
254
)
215
255
return AgentResponse .done (self )
216
256
217
- async def get_relevant_files (
218
- self , user_feedback : Optional [str ] = None , solution_description : Optional [str ] = None
219
- ) -> AgentResponse :
220
- log .debug ("Getting relevant files for the current task" )
221
- await self .send_message ("Figuring out which project files are relevant for the next task ..." )
222
-
223
- llm = self .get_llm ()
224
- convo = (
225
- AgentConvo (self )
226
- .template (
227
- "filter_files" ,
228
- current_task = self .current_state .current_task ,
229
- user_feedback = user_feedback ,
230
- solution_description = solution_description ,
231
- )
232
- .require_schema (RelevantFiles )
233
- )
234
-
235
- llm_response : list [str ] = await llm (convo , parser = JSONParser (RelevantFiles ), temperature = 0 )
236
-
237
- existing_files = {file .path for file in self .current_state .files }
238
- self .next_state .relevant_files = [path for path in llm_response .relevant_files if path in existing_files ]
239
-
240
- return AgentResponse .done (self )
241
-
242
257
def set_next_steps (self , response : TaskSteps , source : str ):
243
258
# For logging/debugging purposes, we don't want to remove the finished steps
244
259
# until we're done with the task.
0 commit comments