mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-10 05:27:54 -05:00
Merge pull request #213 from Pythagora-io/bugfix/ENG-989-more-v2-fixes
(ENG-989) More v2 fixes
This commit is contained in:
@@ -109,14 +109,12 @@ class Architect(BaseAgent):
|
||||
self.next_state.specification = spec
|
||||
telemetry.set("templates", spec.templates)
|
||||
self.next_state.action = ARCHITECTURE_STEP_NAME
|
||||
await self.ui.clear_main_logs()
|
||||
await self.ui.send_front_logs_headers("be_0", ["E2 / T3", "done"], "Setting up backend")
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
"title": "Setting up backend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"labels": ["E2 / T3", "Backend setup", "done"],
|
||||
"project_state_id": "be_0",
|
||||
"labels": ["E2 / T2", "Backend setup", "done"],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import Annotated, Literal, Union
|
||||
from uuid import uuid4
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -24,7 +24,7 @@ from core.db.models.specification import Complexity
|
||||
from core.llm.parser import JSONParser
|
||||
from core.log import get_logger
|
||||
from core.telemetry import telemetry
|
||||
from core.ui.base import ProjectStage
|
||||
from core.ui.base import ProjectStage, pythagora_source
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
@@ -340,13 +340,36 @@ class Developer(ChatWithBreakdownMixin, RelevantFilesMixin, BaseAgent):
|
||||
"task_index": task_index,
|
||||
}
|
||||
)
|
||||
await self.ui.clear_main_logs()
|
||||
await self.ui.send_front_logs_headers(
|
||||
f"be_{task_index}_{task_index + 1}",
|
||||
[f"E{epic_index} / T{task_index}", "Backend", "working"],
|
||||
description,
|
||||
self.current_state.current_task.get("id"),
|
||||
)
|
||||
|
||||
# find latest finished task, send back logs for it being finished
|
||||
tasks_done = [task for task in self.current_state.tasks if task not in self.current_state.unfinished_tasks]
|
||||
previous_task = tasks_done[-1] if tasks_done else None
|
||||
if previous_task:
|
||||
task_convo = await self.state_manager.get_task_conversation_project_states(UUID(previous_task["id"]))
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
"title": previous_task["description"],
|
||||
"project_state_id": str(task_convo[0].id) if task_convo else "be_0",
|
||||
"start_id": str(task_convo[0].id) if task_convo else "be_0",
|
||||
"end_id": str(task_convo[-1].id) if task_convo else "be_0",
|
||||
"labels": [f"E{epic_index} / T{task_index - 1}", "Backend", "done"],
|
||||
}
|
||||
]
|
||||
)
|
||||
await self.ui.send_front_logs_headers(
|
||||
f"be_{epic_index}_{task_index}",
|
||||
[f"E{epic_index} / T{task_index}", "Backend", "working"],
|
||||
previous_task["description"],
|
||||
self.current_state.current_task.get("id"),
|
||||
)
|
||||
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
@@ -373,6 +396,14 @@ class Developer(ChatWithBreakdownMixin, RelevantFilesMixin, BaseAgent):
|
||||
if self.current_state.current_task.get("hardcoded", False):
|
||||
return True
|
||||
|
||||
await self.ui.clear_main_logs()
|
||||
|
||||
if self.current_state.current_task and self.current_state.current_task.get("hardcoded", False):
|
||||
await self.ui.send_message(
|
||||
"Ok, great, you're now starting to build the backend and the first task is to test how the authentication works. You can now register and login. Your data will be saved into the database.",
|
||||
source=pythagora_source,
|
||||
)
|
||||
|
||||
user_response = await self.ask_question(
|
||||
DEV_EXECUTE_TASK,
|
||||
buttons=buttons,
|
||||
|
||||
@@ -54,28 +54,6 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
await self.set_app_details()
|
||||
finished = await self.iterate_frontend()
|
||||
if finished is None:
|
||||
await self.ui.clear_main_logs()
|
||||
await self.ui.send_front_logs_headers("fe_0", ["E2 / T1", "done"], "Building frontend")
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
"title": "Building frontend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"labels": ["E2 / T1", "Frontend", "done"],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
await self.ui.send_front_logs_headers("be_0", ["E2 / T2", "working"], "Setting up backend")
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
"title": "Setting up backend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"labels": ["E2 / T2", "Backend setup", "working"],
|
||||
}
|
||||
]
|
||||
)
|
||||
return AgentResponse.exit(self)
|
||||
|
||||
return await self.end_frontend_iteration(finished)
|
||||
@@ -195,13 +173,20 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
)
|
||||
|
||||
if answer.button == "yes":
|
||||
fe_states = await self.state_manager.get_fe_states()
|
||||
first_fe_state_id = fe_states[0].prev_state_id if fe_states else None
|
||||
|
||||
await self.ui.clear_main_logs()
|
||||
await self.ui.send_front_logs_headers("fe_0", ["E2 / T1", "done"], "Building frontend")
|
||||
await self.ui.send_front_logs_headers(
|
||||
str(first_fe_state_id) if first_fe_state_id else "fe_0",
|
||||
["E2 / T1", "done"],
|
||||
"Building frontend",
|
||||
)
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
"title": "Building frontend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"project_state_id": str(first_fe_state_id) if first_fe_state_id else "fe_0",
|
||||
"labels": ["E2 / T1", "Frontend", "done"],
|
||||
}
|
||||
]
|
||||
@@ -210,7 +195,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
[
|
||||
{
|
||||
"title": "Setting up backend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"project_state_id": "be_0",
|
||||
"labels": ["E2 / T2", "Backend setup", "working"],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -209,7 +209,7 @@ class SpecWriter(BaseAgent):
|
||||
convo = convo.assistant(llm_assisted_description)
|
||||
|
||||
await self.ui.clear_main_logs()
|
||||
await self.ui.send_front_logs_headers("fe_0", ["E2 / T1", "working"], "Building frontend")
|
||||
await self.ui.send_front_logs_headers(str(self.next_state.id), ["E2 / T1", "working"], "Building frontend")
|
||||
await self.ui.send_back_logs(
|
||||
[
|
||||
{
|
||||
@@ -224,7 +224,7 @@ class SpecWriter(BaseAgent):
|
||||
[
|
||||
{
|
||||
"title": "Building frontend",
|
||||
"project_state_id": self.current_state.id,
|
||||
"project_state_id": str(self.next_state.id),
|
||||
"labels": ["E2 / T1", "Frontend", "working"],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import asyncio
|
||||
import json
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -342,22 +341,7 @@ class TechLead(RelevantFilesMixin, BaseAgent):
|
||||
self.next_state.tasks,
|
||||
)
|
||||
|
||||
# await self.ui.send_project_stage({"stage": ProjectStage.OPEN_PLAN})
|
||||
# response = await self.ask_question(
|
||||
# TL_EDIT_DEV_PLAN,
|
||||
# buttons={"done_editing": "I'm done editing, the plan looks good"},
|
||||
# default="done_editing",
|
||||
# buttons_only=True,
|
||||
# extra_info={"edit_plan": True},
|
||||
# )
|
||||
#
|
||||
# self.update_epics_and_tasks(response.text)
|
||||
|
||||
if self.next_state.current_task and self.next_state.current_task.get("hardcoded", False):
|
||||
await self.ui.send_message(
|
||||
"Ok, great, you're now starting to build the backend and the first task is to test how the authentication works. You can now register and login. Your data will be saved into the database.",
|
||||
source=pythagora_source,
|
||||
)
|
||||
self.update_epics_and_tasks()
|
||||
|
||||
await self.ui.send_epics_and_tasks(
|
||||
self.next_state.current_epic["sub_epics"],
|
||||
@@ -384,46 +368,14 @@ class TechLead(RelevantFilesMixin, BaseAgent):
|
||||
file_content = file_content.replace(line + "\n", "")
|
||||
await self.state_manager.save_file(file.path, file_content)
|
||||
|
||||
def update_epics_and_tasks(self, edited_plan_string):
|
||||
edited_plan = json.loads(edited_plan_string)
|
||||
updated_tasks = []
|
||||
|
||||
existing_tasks_map = {task["description"]: task for task in self.next_state.tasks}
|
||||
|
||||
self.next_state.current_epic["sub_epics"] = []
|
||||
for sub_epic_number, sub_epic in enumerate(edited_plan, start=1):
|
||||
self.next_state.current_epic["sub_epics"].append(
|
||||
{
|
||||
"id": sub_epic_number,
|
||||
"description": sub_epic["description"],
|
||||
}
|
||||
)
|
||||
|
||||
for task in sub_epic["tasks"]:
|
||||
original_task = existing_tasks_map.get(task["description"])
|
||||
if original_task and task == original_task:
|
||||
updated_task = original_task.copy()
|
||||
updated_task["sub_epic_id"] = sub_epic_number
|
||||
updated_tasks.append(updated_task)
|
||||
else:
|
||||
updated_tasks.append(
|
||||
{
|
||||
"id": uuid4().hex,
|
||||
"description": task["description"],
|
||||
"instructions": None,
|
||||
"pre_breakdown_testing_instructions": None,
|
||||
"status": TaskStatus.TODO,
|
||||
"sub_epic_id": sub_epic_number,
|
||||
}
|
||||
)
|
||||
|
||||
def update_epics_and_tasks(self):
|
||||
if (
|
||||
self.current_state.current_epic
|
||||
and self.current_state.current_epic.get("source", "") == "app"
|
||||
and self.current_state.knowledge_base.user_options.get("auth", False)
|
||||
):
|
||||
log.debug("Adding auth task to the beginning of the task list")
|
||||
updated_tasks.insert(
|
||||
self.next_state.tasks.insert(
|
||||
0,
|
||||
{
|
||||
"id": uuid4().hex,
|
||||
@@ -473,6 +425,5 @@ class TechLead(RelevantFilesMixin, BaseAgent):
|
||||
"iteration_index": 0,
|
||||
}
|
||||
]
|
||||
self.next_state.tasks = updated_tasks
|
||||
self.next_state.flag_tasks_as_modified()
|
||||
self.next_state.flag_epics_as_modified()
|
||||
|
||||
@@ -761,31 +761,34 @@ class ProjectState(Base):
|
||||
result = await session.execute(query)
|
||||
states = result.scalars().all()
|
||||
|
||||
log.debug(f"Found {len(states)} states with task start action")
|
||||
log.debug(f"Found {len(states)} states with custom action")
|
||||
|
||||
start = -1
|
||||
end = -1
|
||||
|
||||
# for the FIRST task, it is todo in the same state as Create a development plan, while other tasks are "Task #N start" (action)
|
||||
|
||||
# this is done solely to be able to reload to the first task, due to the fact that we need the same project_state_id for the send_back_logs
|
||||
# for the first task, we need to start from the FIRST state that has that task in TODO status
|
||||
# for all other tasks, we need to start from LAST state that has that task in TODO status
|
||||
for i, state in enumerate(states):
|
||||
if start != -1 and end != -1:
|
||||
break
|
||||
if (
|
||||
start == -1
|
||||
and state.current_task
|
||||
and UUID(state.current_task["id"]) == task_id
|
||||
and state.current_task["status"] in [TaskStatus.TODO, TaskStatus.IN_PROGRESS]
|
||||
):
|
||||
start = i
|
||||
if end == -1:
|
||||
for task in state.tasks:
|
||||
if UUID(task["id"]) == task_id and task.get("status") in [
|
||||
TaskStatus.SKIPPED,
|
||||
TaskStatus.DOCUMENTED,
|
||||
TaskStatus.REVIEWED,
|
||||
TaskStatus.DONE,
|
||||
]:
|
||||
end = i
|
||||
break
|
||||
for task in state.tasks:
|
||||
if UUID(task["id"]) == task_id and task.get("status", "") == TaskStatus.TODO:
|
||||
if UUID(task["id"]) == UUID(state.tasks[0]["id"]):
|
||||
# First task: set start only once (first occurrence)
|
||||
if start == -1:
|
||||
start = i
|
||||
else:
|
||||
# Other tasks: update start every time (last occurrence)
|
||||
start = i
|
||||
|
||||
if UUID(task["id"]) == task_id and task.get("status", "") in [
|
||||
TaskStatus.SKIPPED,
|
||||
TaskStatus.DOCUMENTED,
|
||||
TaskStatus.REVIEWED,
|
||||
TaskStatus.DONE,
|
||||
]:
|
||||
end = i
|
||||
|
||||
if end == -1:
|
||||
query = select(ProjectState).where(
|
||||
@@ -807,14 +810,15 @@ class ProjectState(Base):
|
||||
|
||||
# Remove the last state from the list because that state is not yet committed in the database!
|
||||
results = results[:-1]
|
||||
# only return sublist of states, first state should have action like "Task #<task_number> start"
|
||||
index = -1
|
||||
for i, state in enumerate(results):
|
||||
if state.action and "Task #" in state.action and "start" in state.action:
|
||||
index = i
|
||||
break
|
||||
|
||||
return results[index:]
|
||||
# index = -1
|
||||
# for i, state in enumerate(results):
|
||||
# if state.action and "Task #" in state.action and "start" in state.action:
|
||||
# index = i
|
||||
# break
|
||||
#
|
||||
# return results[index:]
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
async def get_fe_states(session: "AsyncSession", branch_id: UUID) -> Optional["ProjectState"]:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
You are the Troubleshooter in a software development team.
|
||||
|
||||
Your primary responsibility is to evaluate the application after each task is implemented, identify any bugs or user-requested changes, and propose an appropriate next step. You act as a QA analyst, bug hunter, and bug fixer all in one. You never assume correctness—you verify it through testing and user feedback.
|
||||
Reference in New Issue
Block a user