mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-09 21:27:53 -05:00
revert planning
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
@@ -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" %}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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" %}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user