revert planning

This commit is contained in:
LeonOstrez
2024-09-16 20:30:45 +02:00
parent cd67593801
commit 1eb8d074e8
11 changed files with 23 additions and 243 deletions

View File

@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from core.agents.base import BaseAgent
from core.agents.convo import AgentConvo
from core.agents.mixins import ActionsConversationMixin, DoneBooleanAction, ReadFilesAction, RelevantFilesMixin
from core.agents.mixins import RelevantFilesMixin
from core.agents.response import AgentResponse, ResponseType
from core.config import PARSE_TASK_AGENT_NAME, TASK_BREAKDOWN_AGENT_NAME
from core.db.models.project_state import IterationStatus, TaskStatus
@@ -60,28 +60,7 @@ class TaskSteps(BaseModel):
steps: list[Step]
class HighLevelInstructions(BaseModel):
high_level_instructions: Optional[str] = Field(
description="Very short high level instructions on how to solve the task."
)
class ListFilesAction(BaseModel):
explanation: Optional[str] = Field(description="Brief explanation for selecting each of the files.")
list_files: Optional[list[str]] = Field(
description="List of files that have to be created or modified during implementation of this task."
)
class DetailedBreakdown(BaseModel):
detailed_breakdown: Optional[str] = Field(description="Full breakdown for implementing the task.")
class BreakdownActions(BaseModel):
action: Union[ReadFilesAction, HighLevelInstructions, ListFilesAction, DetailedBreakdown, DoneBooleanAction]
class Developer(ActionsConversationMixin, RelevantFilesMixin, BaseAgent):
class Developer(RelevantFilesMixin, BaseAgent):
agent_type = "developer"
display_name = "Developer"
@@ -239,27 +218,27 @@ class Developer(ActionsConversationMixin, RelevantFilesMixin, BaseAgent):
current_task_index = self.current_state.tasks.index(current_task)
convo, response = await self.actions_conversation(
data={"task": current_task, "current_task_index": current_task_index},
original_prompt="breakdown",
loop_prompt="breakdown_loop",
schema=BreakdownActions,
llm_config=TASK_BREAKDOWN_AGENT_NAME,
temperature=0,
llm = self.get_llm(TASK_BREAKDOWN_AGENT_NAME, stream_output=True)
convo = AgentConvo(self).template(
"breakdown",
task=current_task,
iteration=None,
current_task_index=current_task_index,
docs=self.current_state.docs,
)
response: str = await llm(convo)
await self.get_relevant_files(None, response)
instructions = response["detailed_breakdown"]
await self.send_message("Breakdown finished!")
await self.send_message(instructions)
self.next_state.tasks[current_task_index] = {
**current_task,
"instructions": instructions,
"instructions": response,
}
self.next_state.flag_tasks_as_modified()
llm = self.get_llm(PARSE_TASK_AGENT_NAME)
await self.send_message("Breaking down the task into steps ...")
convo.assistant(instructions).template("parse_task").require_schema(TaskSteps)
convo.assistant(response).template("parse_task").require_schema(TaskSteps)
response: TaskSteps = await llm(convo, parser=JSONParser(TaskSteps), temperature=0)
# There might be state leftovers from previous tasks that we need to clean here

View File

@@ -1,4 +1,3 @@
import random
from typing import List, Optional, Union
from pydantic import BaseModel, Field
@@ -6,7 +5,6 @@ from pydantic import BaseModel, Field
from core.agents.convo import AgentConvo
from core.agents.response import AgentResponse
from core.config import GET_RELEVANT_FILES_AGENT_NAME, TROUBLESHOOTER_BUG_REPORT
from core.config.magic_words import THINKING_LOGS
from core.llm.parser import JSONParser
from core.log import get_logger
@@ -126,78 +124,3 @@ class RelevantFilesMixin:
self.next_state.relevant_files = relevant_files
return AgentResponse.done(self)
class ActionsConversationMixin:
"""
Provides a method to loop in conversation until done.
"""
async def actions_conversation(
self,
data: any,
original_prompt: str,
loop_prompt: str,
schema,
llm_config,
temperature: Optional[float] = 0.5,
max_convo_length: Optional[int] = 20,
stream_llm_output: Optional[bool] = False,
) -> tuple[AgentConvo, any]:
"""
Loop in conversation until done.
:param data: The initial data to pass into the conversation.
:param original_prompt: The prompt template name for the initial request.
:param loop_prompt: The prompt template name for the looped requests.
:param schema: The schema class to enforce the structure of the LLM response.
:param llm_config: The LLM configuration to use for the conversation.
:param temperature: The temperature to use for the LLM response.
:param max_convo_length: The maximum number of messages to allow in the conversation.
:return: A tuple of the conversation and the final aggregated data.
"""
llm = self.get_llm(llm_config, stream_output=stream_llm_output)
convo = (
AgentConvo(self)
.template(
original_prompt,
**data,
)
.require_schema(schema)
)
response = await llm(convo, parser=JSONParser(schema), temperature=temperature)
convo.remove_last_x_messages(1)
convo.assistant(response.original_response)
# Initialize loop_data to store the cumulative data from the loop
loop_data = {
attr: getattr(response.action, attr, None) for attr in dir(response.action) if not attr.startswith("_")
}
loop_data["read_files"] = getattr(response.action, "read_files", [])
done = getattr(response.action, "done", False)
# Keep working on the task until `done` or we reach 20 messages in convo.
while not done and len(convo.messages) < max_convo_length:
await self.send_message(random.choice(THINKING_LOGS))
convo.template(
loop_prompt,
**loop_data,
).require_schema(schema)
response = await llm(convo, parser=JSONParser(schema), temperature=temperature)
convo.remove_last_x_messages(1)
convo.assistant(response.original_response)
# Update loop_data with new information, replacing everything except for 'read_files'
for attr in dir(response.action):
if not attr.startswith("_"):
current_value = getattr(response.action, attr, None)
if attr == "read_files" and current_value:
loop_data[attr].extend(item for item in current_value if item not in loop_data[attr])
else:
loop_data[attr] = current_value
done = getattr(response.action, "done", False)
return convo, loop_data

View File

@@ -1,11 +1,9 @@
from typing import Optional, Union
from uuid import uuid4
from pydantic import BaseModel, Field
from core.agents.base import BaseAgent
from core.agents.convo import AgentConvo
from core.agents.mixins import ActionsConversationMixin, DoneBooleanAction, ReadFilesAction
from core.agents.response import AgentResponse
from core.config import TECH_LEAD_PLANNING
from core.db.models.project_state import TaskStatus
@@ -32,24 +30,6 @@ class DevelopmentPlan(BaseModel):
plan: list[Epic] = Field(description="List of epics that need to be done to implement the entire plan.")
class HighLevelPlanAction(BaseModel):
high_level_plan: Optional[str] = Field(
description="Short high level plan on how to systematically approach this app planning."
)
class DevelopmentPlanAction(BaseModel):
development_plan: list[Epic] = Field(description="List of epics that need to be done to implement the entire app.")
class ReviewPlanAction(BaseModel):
review_plan: str = Field(description="Review if everything is ok with the current plan.")
class PlanningActions(BaseModel):
action: Union[ReadFilesAction, HighLevelPlanAction, DevelopmentPlanAction, ReviewPlanAction, DoneBooleanAction]
class EpicPlan(BaseModel):
plan: list[Task] = Field(description="List of tasks that need to be done to implement the entire epic.")
@@ -61,7 +41,7 @@ class UpdatedDevelopmentPlan(BaseModel):
plan: list[Task] = Field(description="List of unfinished epics.")
class TechLead(ActionsConversationMixin, BaseAgent):
class TechLead(BaseAgent):
agent_type = "tech-lead"
display_name = "Tech Lead"

View File

@@ -39,7 +39,6 @@ CODE_REVIEW_AGENT_NAME = "CodeMonkey.code_review"
DESCRIBE_FILES_AGENT_NAME = "CodeMonkey.describe_files"
CHECK_LOGS_AGENT_NAME = "BugHunter.check_logs"
PARSE_TASK_AGENT_NAME = "Developer.parse_task"
PLANNING_AGENT_NAME = "TechLead.plan_epic"
TASK_BREAKDOWN_AGENT_NAME = "Developer.breakdown_current_task"
TROUBLESHOOTER_BUG_REPORT = "Troubleshooter.generate_bug_report"
TROUBLESHOOTER_GET_RUN_COMMAND = "Troubleshooter.get_run_command"
@@ -349,11 +348,6 @@ class Config(_StrictModel):
model="gpt-4-0125-preview",
temperature=0.0,
),
PLANNING_AGENT_NAME: AgentLLMConfig(
provider=LLMProvider.ANTHROPIC,
model="claude-3-5-sonnet-20240620",
temperature=0.5,
),
SPEC_WRITER_AGENT_NAME: AgentLLMConfig(
provider=LLMProvider.OPENAI,
model="gpt-4-0125-preview",

View File

@@ -53,7 +53,7 @@ class LLMRequest(Base):
Store the request log in the database.
Note this just creates the request log object. It is committed to the
database only when the DB session itself is committed.
database only when the DB session itself is comitted.
:param project_state: Project state to associate the request log with.
:param agent: Agent that made the request (if the caller was an agent).

View File

@@ -22,6 +22,7 @@ DO NOT specify commands to create any folders or files, they will be created aut
Never use the port 5000 to run the app, it's reserved.
--IMPLEMENTATION INSTRUCTIONS--
We've broken the development of this {% if state.epics|length > 1 %}feature{% else %}app{% endif %} down to these tasks:
```
{% for task in state.tasks %}
@@ -42,4 +43,3 @@ Here is how this task should be tested:
{% if current_task_index != 0 %}All previous tasks are finished and you don't have to work on them.{% endif %}
Now, tell me all the code that needs to be written to implement ONLY this task and have it fully working and all commands that need to be run to implement this task.
{% include "partials/breakdown_actions.prompt" %}

View File

@@ -1,32 +0,0 @@
Continue working on creating detailed breakdown for this task, listing everything that needs to be done for this task to be successfully implemented.
Focus on previous messages in this conversation so that you don't repeat yourself (e.g. don't `read_files` that you already read previously because they didn't change in meantime).
This is your progress so far:
{% if high_level_instructions is defined and high_level_instructions %}- You created high_level_instructions:
```
{{high_level_instructions}}
```
{% endif %}
{% if list_files is defined and list_files %}- You listed these files for now:
```
{{list_files}}
```
{% if explanation is defined and explanation %}
With this explanation:
`{{explanation}}`
{% endif %}{% endif %}
{% if read_files is defined and read_files %}- You read these files:
```
{{read_files}}
```
{% endif %}
{% if detailed_breakdown is defined and detailed_breakdown %}- You created this detailed_breakdown:
---START_OF_CURRENT_BREAKDOWN---
{{detailed_breakdown}}
---END_OF_CURRENT_BREAKDOWN---
{% endif %}
{% include "partials/breakdown_actions.prompt" %}

View File

@@ -1,16 +0,0 @@
Your job is to figure out all details that have to be implemented for this task to be completed successfully.
Think step by step what information do you need, what files have to be implemented for this task and what has to be implemented in those files. If you need to see content of some other files in project, you can do so with `read_files` action. Start by giving high level instructions on what needs to be done. At any point you can ask to see content of some files you haven't seen before and might be relevant for this task. Also, you can change your mind and update high level instructions, list of files that have to be created/modified or even change detailed breakdown if you noticed you missed something.
While doing this you have access to the following actions:
- `read_files` - to read the content of the files
- `high_level_instructions` - create or update high level instructions
- `list_files` - list all files that need to be created or updated
- `detailed_breakdown` - create full breakdown for this task, including the code snippets that have to be implemented
- `done` - boolean to indicate when you're done with the breakdown
You can use only one action at a time. After each action, you will be asked what you want to do next. You can use same action you already used before (e.g. `list_files` if you want to add more files or remove some from the list).
You must read the file using the `read_files` action before you can list it in the `list_files` action.
You must read the file using the `read_files` action before you can suggest changes of that file in the `detailed_breakdown` action.
Creating detailed breakdown is the most important part of your task. While working on detailed breakdown, make sure that you don't miss anything and that you provide all necessary details for this task to be completed successfully while focusing not to break any existing functionality that app might have. Detailed breakdown should be as detailed as possible, so that anyone can follow it and implement it without any additional questions. Do not leave anything for interpretation, e.g. if something can be done in multiple ways, specify which way should be used and be as clear as possible. You can put small code snippets (do not code full files, developer will do that) that have to be implemented in the files if you think that will help to understand the task better.
If you want to finish creating the breakdown, just use action to set boolean flag `done` to true.

View File

@@ -1,14 +0,0 @@
Your job is to figure out all epics that have to be implemented for this app to work flawlessly.
Think step by step what information do you need, what epics have to be implemented to have fully working app. Start by giving high level plan to give brief overview of how the plan will be structured. You can always change your mind and update high level plan, list of files that have to be created/modified or even change detailed breakdown if you noticed you missed something.
While doing this you have access to the following actions:
- `read_files` - to read the content of the files if there are any files in the project
- `high_level_plan` - create high level plan
- `development_plan` - Create full development plan for this app that consists of all epics that have to be implemented.
- `review_plan` - Review the current development plan and if changes are needed, explain here in details what has to be changed.
- `done` - boolean to indicate when you're done with the breakdown
You can use only one action at a time. After each action, you will be asked what you want to do next. You can use same action you already used before only if you need to make a change to that action (e.g. `development_plan` only if you want to add, update or remove epics from the plan. Do not use same action to recreate exactly same plan.). Look at previous messages in conversation to see what you already did so you don't repeat yourself.
Creating development plan is the most important part of your task and has to be done thoroughly. Once development plan is created you have to review that plan using `review_plan` action and if changes are needed, explain what has to be changed.
Once the development plan is done and review of plan is done and doesn't need any changes, use action `done` and set it to true.

View File

@@ -1,33 +0,0 @@
Continue working on creating development plan, listing everything that needs to be done for this app to be successfully implemented.
Focus on previous messages in this conversation so that you don't repeat yourself (e.g. don't `read_files` that you already read previously because they didn't change in meantime).
{% include "partials/project_tasks.prompt" %}
This is your progress so far:
{% if high_level_plan is defined and high_level_plan %}- You created high_level_plan:
```
{{high_level_plan}}
```
{% endif %}
{% if read_files is defined and read_files %}- You read these files:
```
{{read_files}}
```
{% endif %}
{% if development_plan is defined and development_plan %}- You created this development_plan:
---START_OF_CURRENT_DEVELOPMENT_PLAN---
{% for epic in development_plan %}
{{ loop.index }}. {{ epic.description }}
{% endfor %}
---END_OF_CURRENT_DEVELOPMENT_PLAN---
{% endif %}
{% if review_plan is defined and review_plan %}- You reviewed the plan:
```
{{review_plan}}
```
{% endif %}
{% include "partials/planning_actions.prompt" %}

View File

@@ -1,7 +1,7 @@
import pytest
from core.agents.response import ResponseType
from core.agents.tech_lead import Epic, HighLevelPlanAction, PlanningActions, TechLead, UpdatedDevelopmentPlan
from core.agents.tech_lead import DevelopmentPlan, Epic, TechLead, UpdatedDevelopmentPlan
from core.db.models import Complexity
from core.db.models.project_state import TaskStatus
from core.ui.base import UserInput
@@ -65,9 +65,7 @@ async def test_ask_for_feature(agentcontext):
assert sm.current_state.epics[1]["completed"] is False
# todo fix this test
@pytest.mark.skip(reason="Temporary")
@pytest.mark.asyncio
async def test_plan_epic(agentcontext):
"""
If called and there's an incomplete epic, the TechLead agent should plan the epic.
@@ -87,10 +85,11 @@ async def test_plan_epic(agentcontext):
tl = TechLead(sm, ui)
tl.get_llm = mock_get_llm(
return_value=PlanningActions(
action=HighLevelPlanAction(
high_level_plan="High level plan",
)
return_value=DevelopmentPlan(
plan=[
Epic(description="Task 1"),
Epic(description="Task 2"),
]
)
)
response = await tl.run()