mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-10 13:37:55 -05:00
@@ -3,3 +3,4 @@ MAX_RECURSION_LAYER = 3
|
||||
MIN_COMMAND_RUN_TIME = 2000 # 2sec
|
||||
MAX_COMMAND_RUN_TIME = 60000 # 1min
|
||||
MAX_COMMAND_OUTPUT_LENGTH = 50000
|
||||
MAX_QUESTIONS_FOR_BUG_REPORT = 5
|
||||
|
||||
@@ -49,3 +49,32 @@ IGNORE_PATHS = DEFAULT_IGNORE_PATHS + [
|
||||
]
|
||||
IGNORE_SIZE_THRESHOLD = 50000 # 50K+ files are ignored by default
|
||||
PROMPT_DATA_TO_IGNORE = {'directory_tree', 'name'}
|
||||
|
||||
|
||||
EXAMPLE_PROJECT_DESCRIPTION = (
|
||||
"A simple webchat application in node/express using MongoDB. "
|
||||
"Use Bootstrap and jQuery on the frontend, for a simple, clean UI. "
|
||||
"Use socket.io for real-time communication between backend and frontend.\n\n"
|
||||
"Visiting http://localhost:3000/, users must first log in or create an account using "
|
||||
"a username and a password (no email required).\n\n"
|
||||
"Once authenticated, on the home screen users see list of active chat rooms and a button to create a new chat. "
|
||||
"They can either click a link to one of the chat rooms which redirects them to `/<chat-id>/` "
|
||||
"or click the button to create a new chat. Creating a new chat should ask for the chat name, "
|
||||
"and then create a new chat with that name (which doesn't need to be unique), and a unique chat id. "
|
||||
"The user should then be redirected to the chat page.\n\n"
|
||||
"Chat page should have the chat name as the title. There's no possibility to edit chat name. "
|
||||
"Below that, show previous messages in the chat (these should get loaded from the database "
|
||||
"whenever the user visits the page so the user sees previous conversation in that chat - "
|
||||
"no pagination, entire history should be loaded). "
|
||||
"User has a text field and a button 'send' to send the message "
|
||||
"(pressing enter in the text field should also send the message). "
|
||||
"There's also a button to change user's nickname (default is equal to username, "
|
||||
"there's no need to store the nickname in the user's profile).\n\n"
|
||||
"Sent messages should be immediately shown to other participants in the same chat (use socket.io), "
|
||||
"and stored in the database (forever - no message expiry). "
|
||||
"All messages are text-only: no image upload, no embedding, no special markup in the messages. "
|
||||
"There's no message size limit. Also, there's no need to notify/alert user of new messages, or keep track of unread messages.\n\n"
|
||||
"All channels are available to all authenticated users, there are no private messages. "
|
||||
"Anonymous users can't see or join any chat rooms, the can only log in or create an account. "
|
||||
"No moderation, filtering or any admin functionality is required. Keep everything else as simple as possible."
|
||||
)
|
||||
|
||||
@@ -476,7 +476,7 @@ GET_DOCUMENTATION_FILE = {
|
||||
REVIEW_CHANGES = {
|
||||
'definitions': [{
|
||||
'name': 'review_diff',
|
||||
'description': 'Review a unified diff and select hunks to apply.',
|
||||
'description': 'Review a unified diff and select hunks to apply or rework.',
|
||||
'parameters': {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -491,12 +491,12 @@ REVIEW_CHANGES = {
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"desciprion": "Reason for applying or ignoring this hunk."
|
||||
"description": "Reason for applying or ignoring this hunk, or for asking for it to be reworked."
|
||||
},
|
||||
"decision": {
|
||||
"type": "string",
|
||||
"enum": ["apply", "ignore"],
|
||||
"description": "Whether to apply this hunk (if it's a valid change) or ignore it."
|
||||
"enum": ["apply", "ignore", "rework"],
|
||||
"description": "Whether to apply this hunk (if it's a valid change with no problems), rework (a valid change but does something incorrectly), or ignore it (unwanted change)."
|
||||
}
|
||||
},
|
||||
"required": ["number", "reason", "decision"],
|
||||
@@ -512,3 +512,40 @@ REVIEW_CHANGES = {
|
||||
}
|
||||
}],
|
||||
}
|
||||
|
||||
GET_BUG_REPORT_MISSING_DATA = {
|
||||
'definitions': [{
|
||||
'name': 'bug_report_missing_data',
|
||||
'description': 'Review bug report and identify missing data. List questions that need to be answered to proceed with the bug fix. If no additional questions are needed missing_data should be an empty array.',
|
||||
'parameters': {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reasoning": {
|
||||
"type": "string",
|
||||
"description": "Reasoning for asking these questions or for not asking any questions."
|
||||
},
|
||||
"missing_data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": ["general", "frontend", "backend", "database", "devops", "other"],
|
||||
"description": "Category of the question."
|
||||
},
|
||||
"question": {
|
||||
"type": "string",
|
||||
"description": "Very clear question that needs to be answered to have good bug report.",
|
||||
},
|
||||
},
|
||||
"required": ["category", "question"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
}
|
||||
},
|
||||
"required": ["reasoning", "missing_data"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}],
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ MESSAGE_TYPE = {
|
||||
LOCAL_IGNORE_MESSAGE_TYPES = [
|
||||
'info',
|
||||
'project_folder_name',
|
||||
'run_command',
|
||||
'button',
|
||||
'buttons-only',
|
||||
'exit',
|
||||
|
||||
@@ -10,7 +10,7 @@ from const.messages import CHECK_AND_CONTINUE, AFFIRMATIVE_ANSWERS, NEGATIVE_ANS
|
||||
from utils.style import color_yellow_bold, color_cyan, color_white_bold, color_red_bold
|
||||
from const.common import STEPS
|
||||
from database.database import delete_unconnected_steps_from, delete_all_app_development_data, \
|
||||
get_all_app_development_steps, delete_all_subsequent_steps
|
||||
get_all_app_development_steps, delete_all_subsequent_steps, get_features_by_app_id
|
||||
from const.ipc import MESSAGE_TYPE
|
||||
from prompts.prompts import ask_user
|
||||
from helpers.exceptions import TokenLimitError, GracefulExit
|
||||
@@ -88,6 +88,8 @@ class Project:
|
||||
self.package_dependencies = []
|
||||
self.project_template = None
|
||||
self.development_plan = None
|
||||
self.previous_features = None
|
||||
self.current_feature = None
|
||||
self.dot_pilot_gpt = DotGptPilot(log_chat_completions=True)
|
||||
|
||||
if os.getenv("AUTOFIX_FILE_PATHS", "").lower() in ["true", "1", "yes"]:
|
||||
@@ -203,6 +205,8 @@ class Project:
|
||||
feature_description = ''
|
||||
if not self.features_to_load:
|
||||
self.finish_loading()
|
||||
|
||||
self.previous_features = get_features_by_app_id(self.args['app_id'])
|
||||
if not self.skip_steps:
|
||||
feature_description = ask_user(self, "Project is finished! Do you want to add any features or changes? "
|
||||
"If yes, describe it here and if no, just press ENTER",
|
||||
@@ -240,6 +244,7 @@ class Project:
|
||||
feature_description = current_feature['prompt_data']['feature_description']
|
||||
self.features_to_load = []
|
||||
|
||||
self.current_feature = feature_description
|
||||
self.developer.start_coding()
|
||||
self.tech_lead.create_feature_summary(feature_description)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from helpers.AgentConvo import AgentConvo
|
||||
from helpers.Agent import Agent
|
||||
from helpers.files import get_file_contents
|
||||
from const.function_calls import GET_FILE_TO_MODIFY, REVIEW_CHANGES
|
||||
from logger.logger import logger
|
||||
|
||||
from utils.exit import trace_code_event
|
||||
from utils.telemetry import telemetry
|
||||
@@ -97,13 +98,25 @@ class CodeMonkey(Agent):
|
||||
files,
|
||||
)
|
||||
|
||||
# Review the changes and only apply changes that are useful/approved
|
||||
if content and content != file_content:
|
||||
content = self.review_change(convo, code_change_description, file_name, file_content, content)
|
||||
for i in range(MAX_REVIEW_RETRIES):
|
||||
if not content or content == file_content:
|
||||
# There are no changes or there was problem talking with the LLM, we're done here
|
||||
break
|
||||
|
||||
content, rework_feedback = self.review_change(convo, code_change_description, file_name, file_content, content)
|
||||
if not rework_feedback:
|
||||
# No rework needed, we're done here
|
||||
break
|
||||
|
||||
content = convo.send_message('development/review_feedback.prompt', {
|
||||
"content": content,
|
||||
"original_content": file_content,
|
||||
"rework_feedback": rework_feedback,
|
||||
})
|
||||
if content:
|
||||
content = self.remove_backticks(content)
|
||||
|
||||
# If we have changes, update the file
|
||||
# TODO: if we *don't* have changes, we might want to retry the whole process (eg. have the reviewer
|
||||
# explicitly reject the whole PR and use that as feedback in implement_changes.prompt)
|
||||
if content and content != file_content:
|
||||
if not self.project.skip_steps:
|
||||
delta_lines = len(content.splitlines()) - len(file_content.splitlines())
|
||||
@@ -143,12 +156,22 @@ class CodeMonkey(Agent):
|
||||
"file_name": file_name,
|
||||
"files": files,
|
||||
})
|
||||
convo.remove_last_x_messages(2)
|
||||
return self.remove_backticks(llm_response)
|
||||
|
||||
@staticmethod
|
||||
def remove_backticks(content: str) -> str:
|
||||
"""
|
||||
Remove optional backticks from the beginning and end of the content.
|
||||
|
||||
:param content: content to remove backticks from
|
||||
:return: content without backticks
|
||||
"""
|
||||
start_pattern = re.compile(r"^\s*```([a-z0-9]+)?\n")
|
||||
end_pattern = re.compile(r"\n```\s*$")
|
||||
llm_response = start_pattern.sub("", llm_response)
|
||||
llm_response = end_pattern.sub("", llm_response)
|
||||
convo.remove_last_x_messages(2)
|
||||
return llm_response
|
||||
content = start_pattern.sub("", content)
|
||||
content = end_pattern.sub("", content)
|
||||
return content
|
||||
|
||||
def identify_file_to_change(self, code_changes_description: str, files: list[dict]) -> str:
|
||||
"""
|
||||
@@ -172,7 +195,7 @@ class CodeMonkey(Agent):
|
||||
file_name: str,
|
||||
old_content: str,
|
||||
new_content: str
|
||||
) -> str:
|
||||
) -> tuple[str, str]:
|
||||
"""
|
||||
Review changes that were applied to the file.
|
||||
|
||||
@@ -184,7 +207,7 @@ class CodeMonkey(Agent):
|
||||
:param file_name: name of the file being modified
|
||||
:param old_content: old file content
|
||||
:param new_content: new file content (with proposed changes)
|
||||
:return: file content update with approved changes
|
||||
:return: tuple with file content update with approved changes, and review feedback
|
||||
|
||||
Diff hunk explanation: https://www.gnu.org/software/diffutils/manual/html_node/Hunks.html
|
||||
"""
|
||||
@@ -200,20 +223,25 @@ class CodeMonkey(Agent):
|
||||
messages_to_remove = 2
|
||||
|
||||
for i in range(MAX_REVIEW_RETRIES):
|
||||
reasons = {}
|
||||
ids_to_apply = set()
|
||||
ids_to_ignore = set()
|
||||
ids_to_rework = set()
|
||||
for hunk in llm_response.get("hunks", []):
|
||||
reasons[hunk["number"] - 1] = hunk["reason"]
|
||||
if hunk.get("decision", "").lower() == "apply":
|
||||
ids_to_apply.add(hunk["number"] - 1)
|
||||
elif hunk.get("decision", "").lower() == "ignore":
|
||||
ids_to_ignore.add(hunk["number"] - 1)
|
||||
elif hunk.get("decision", "").lower() == "rework":
|
||||
ids_to_rework.add(hunk["number"] - 1)
|
||||
|
||||
n_hunks = len(hunks)
|
||||
n_review_hunks = len(ids_to_apply | ids_to_ignore)
|
||||
n_review_hunks = len(reasons)
|
||||
if n_review_hunks == n_hunks:
|
||||
break
|
||||
elif n_review_hunks < n_hunks:
|
||||
error = "Not all hunks have been reviewed. Please review all hunks and add 'apply' or 'ignore' decision for each."
|
||||
error = "Not all hunks have been reviewed. Please review all hunks and add 'apply', 'ignore' or 'rework' decision for each."
|
||||
elif n_review_hunks > n_hunks:
|
||||
error = f"Your review contains more hunks ({n_review_hunks}) than in the original diff ({n_hunks}). Note that one hunk may have multiple changed lines."
|
||||
|
||||
@@ -235,23 +263,37 @@ class CodeMonkey(Agent):
|
||||
hunks_to_apply = [ h for i, h in enumerate(hunks) if i in ids_to_apply ]
|
||||
diff_log = f"--- {file_name}\n+++ {file_name}\n" + "\n".join(hunks_to_apply)
|
||||
|
||||
hunks_to_rework = [ (i, h) for i, h in enumerate(hunks) if i in ids_to_rework ]
|
||||
review_log = "\n\n".join([
|
||||
f"## Change\n```{hunk}```\nReviewer feedback:\n{reasons[i]}" for (i, hunk) in hunks_to_rework
|
||||
]) + "\n\nReview notes:\n" + llm_response["review_notes"]
|
||||
|
||||
if len(hunks_to_apply) == len(hunks):
|
||||
print("Applying entire change")
|
||||
return new_content
|
||||
logger.info(f"Applying entire change to {file_name}")
|
||||
return new_content, None
|
||||
|
||||
elif len(hunks_to_apply) == 0:
|
||||
print("Reviewer has doubts, but applying all proposed changes anyway")
|
||||
trace_code_event(
|
||||
"modify-file-review-reject-all",
|
||||
{
|
||||
"file": file_name,
|
||||
"original": old_content,
|
||||
"diff": f"--- {file_name}\n+++ {file_name}\n" + "\n".join(hunks),
|
||||
}
|
||||
)
|
||||
return new_content
|
||||
if hunks_to_rework:
|
||||
print(f"Requesting rework for {len(hunks_to_rework)} changes with reason: {llm_response['review_notes']}")
|
||||
logger.info(f"Requesting rework for {len(hunks_to_rework)} changes to {file_name} (0 hunks to apply)")
|
||||
return old_content, review_log
|
||||
else:
|
||||
# If everything can be safely ignored, it's probably because the files already implement the changes
|
||||
# from previous tasks (which can happen often). Insisting on a change here is likely to cause problems.
|
||||
print(f"Rejecting entire change with reason: {llm_response['review_notes']}")
|
||||
logger.info(f"Rejecting entire change to {file_name} with reason: {llm_response['review_notes']}")
|
||||
return old_content, None
|
||||
|
||||
print("Applying code change:\n" + diff_log)
|
||||
logger.info(f"Applying code change to {file_name}:\n{diff_log}")
|
||||
new_content = self.apply_diff(file_name, old_content, hunks_to_apply, new_content)
|
||||
if hunks_to_rework:
|
||||
print(f"Requesting rework for {len(hunks_to_rework)} changes with reason: {llm_response['review_notes']}")
|
||||
logger.info(f"Requesting further rework for {len(hunks_to_rework)} changes to {file_name}")
|
||||
return new_content, review_log
|
||||
else:
|
||||
print("Applying code change:\n" + diff_log)
|
||||
return self.apply_diff(file_name, old_content, hunks_to_apply, new_content)
|
||||
return new_content, None
|
||||
|
||||
@staticmethod
|
||||
def get_diff_hunks(file_name: str, old_content: str, new_content: str) -> list[str]:
|
||||
|
||||
@@ -15,7 +15,7 @@ from utils.style import (
|
||||
color_white_bold
|
||||
)
|
||||
from helpers.exceptions import TokenLimitError
|
||||
from const.code_execution import MAX_COMMAND_DEBUG_TRIES
|
||||
from const.code_execution import MAX_COMMAND_DEBUG_TRIES, MAX_QUESTIONS_FOR_BUG_REPORT
|
||||
from helpers.exceptions import TooDeepRecursionError
|
||||
from helpers.Debugger import Debugger
|
||||
from utils.questionary import styled_text
|
||||
@@ -26,7 +26,8 @@ from helpers.Agent import Agent
|
||||
from helpers.AgentConvo import AgentConvo
|
||||
from utils.utils import should_execute_step, array_of_objects_to_string, generate_app_data
|
||||
from helpers.cli import run_command_until_success, execute_command_and_check_cli_response
|
||||
from const.function_calls import EXECUTE_COMMANDS, GET_TEST_TYPE, IMPLEMENT_TASK, COMMAND_TO_RUN, ALTERNATIVE_SOLUTIONS
|
||||
from const.function_calls import (EXECUTE_COMMANDS, GET_TEST_TYPE, IMPLEMENT_TASK, COMMAND_TO_RUN,
|
||||
ALTERNATIVE_SOLUTIONS, GET_BUG_REPORT_MISSING_DATA)
|
||||
from database.database import save_progress, get_progress_steps, update_app_status
|
||||
from utils.telemetry import telemetry
|
||||
from prompts.prompts import ask_user
|
||||
@@ -134,7 +135,9 @@ class Developer(Agent):
|
||||
"files": self.project.get_all_coded_files(),
|
||||
"architecture": self.project.architecture,
|
||||
"technologies": self.project.system_dependencies + self.project.package_dependencies,
|
||||
"task_type": 'feature' if self.project.finished else 'app'
|
||||
"task_type": 'feature' if self.project.finished else 'app',
|
||||
"previous_features": self.project.previous_features,
|
||||
"current_feature": self.project.current_feature,
|
||||
})
|
||||
|
||||
instructions_prefix = " ".join(instructions.split()[:5])
|
||||
@@ -602,6 +605,7 @@ class Developer(Agent):
|
||||
return {"success": True, "user_input": user_feedback}
|
||||
|
||||
if user_feedback is not None:
|
||||
user_feedback = self.bug_report_generator(user_feedback)
|
||||
stuck_in_loop = user_feedback.startswith(STUCK_IN_LOOP)
|
||||
if stuck_in_loop:
|
||||
# Remove the STUCK_IN_LOOP prefix from the user feedback
|
||||
@@ -639,7 +643,9 @@ class Developer(Agent):
|
||||
"next_solution_to_try": next_solution_to_try,
|
||||
"alternative_solutions_to_current_issue": alternative_solutions_to_current_issue,
|
||||
"tried_alternative_solutions_to_current_issue": tried_alternative_solutions_to_current_issue,
|
||||
"iteration_count": iteration_count
|
||||
"iteration_count": iteration_count,
|
||||
"previous_features": self.project.previous_features,
|
||||
"current_feature": self.project.current_feature,
|
||||
})
|
||||
|
||||
llm_solutions.append({
|
||||
@@ -660,6 +666,61 @@ class Developer(Agent):
|
||||
task_steps = llm_response['tasks']
|
||||
self.execute_task(iteration_convo, task_steps, is_root_task=True)
|
||||
|
||||
def bug_report_generator(self, user_feedback):
|
||||
"""
|
||||
Generate a bug report from the user feedback.
|
||||
|
||||
:param user_feedback: The user feedback.
|
||||
:return: The bug report.
|
||||
"""
|
||||
bug_report_convo = AgentConvo(self)
|
||||
function_uuid = str(uuid.uuid4())
|
||||
bug_report_convo.save_branch(function_uuid)
|
||||
questions_and_answers = []
|
||||
while True:
|
||||
llm_response = bug_report_convo.send_message('development/bug_report.prompt', {
|
||||
"user_feedback": user_feedback,
|
||||
"app_summary": self.project.project_description,
|
||||
"files": self.project.get_all_coded_files(),
|
||||
"questions_and_answers": questions_and_answers,
|
||||
}, GET_BUG_REPORT_MISSING_DATA)
|
||||
|
||||
missing_data = llm_response['missing_data']
|
||||
if len(missing_data) == 0:
|
||||
break
|
||||
|
||||
length_before = len(questions_and_answers)
|
||||
for missing_data_item in missing_data:
|
||||
if self.project.check_ipc():
|
||||
print(missing_data_item['question'], type='verbose')
|
||||
print('continue/skip question', type='button')
|
||||
if self.run_command:
|
||||
print(self.run_command, type='run_command')
|
||||
|
||||
answer = ask_user(self.project, missing_data_item['question'], require_some_input=False)
|
||||
if answer.lower() == 'skip question' or answer.lower() == '' or answer.lower() == 'continue':
|
||||
continue
|
||||
|
||||
questions_and_answers.append({
|
||||
"question": missing_data_item['question'],
|
||||
"answer": answer
|
||||
})
|
||||
|
||||
# if user skips all questions or if we got more than 4 answers, we don't want to get stuck in infinite loop
|
||||
if length_before == len(questions_and_answers) or len(questions_and_answers) >= MAX_QUESTIONS_FOR_BUG_REPORT:
|
||||
break
|
||||
bug_report_convo.load_branch(function_uuid)
|
||||
|
||||
if len(questions_and_answers):
|
||||
bug_report_summary_convo = AgentConvo(self)
|
||||
user_feedback = bug_report_summary_convo.send_message('development/bug_report_summary.prompt', {
|
||||
"app_summary": self.project.project_description,
|
||||
"user_feedback": user_feedback,
|
||||
"questions_and_answers": questions_and_answers,
|
||||
})
|
||||
|
||||
return user_feedback
|
||||
|
||||
def review_task(self):
|
||||
"""
|
||||
Review all task changes and refactor big files.
|
||||
@@ -700,6 +761,8 @@ class Developer(Agent):
|
||||
"user_input": self.user_feedback,
|
||||
"modified_files": self.modified_files,
|
||||
"files_at_start_of_task": files_at_start_of_task,
|
||||
"previous_features": self.project.previous_features,
|
||||
"current_feature": self.project.current_feature,
|
||||
})
|
||||
|
||||
instructions_prefix = " ".join(review.split()[:5])
|
||||
@@ -867,6 +930,8 @@ class Developer(Agent):
|
||||
"user_input": user_feedback,
|
||||
"previous_solutions": previous_solutions,
|
||||
"tried_alternative_solutions_to_current_issue": tried_alternative_solutions_to_current_issue,
|
||||
"previous_features": self.project.previous_features,
|
||||
"current_feature": self.project.current_feature,
|
||||
}, ALTERNATIVE_SOLUTIONS)
|
||||
|
||||
next_solution_to_try_index = self.ask_user_for_next_solution(response['alternative_solutions'])
|
||||
|
||||
@@ -9,6 +9,7 @@ from utils.files import setup_workspace
|
||||
from prompts.prompts import ask_for_app_type, ask_for_main_app_definition, ask_user
|
||||
from const.llm import END_RESPONSE
|
||||
from const.messages import MAX_PROJECT_NAME_LENGTH
|
||||
from const.common import EXAMPLE_PROJECT_DESCRIPTION
|
||||
|
||||
PROJECT_DESCRIPTION_STEP = 'project_description'
|
||||
USER_STORIES_STEP = 'user_stories'
|
||||
@@ -39,31 +40,41 @@ class ProductOwner(Agent):
|
||||
|
||||
# PROJECT DESCRIPTION
|
||||
self.project.current_step = PROJECT_DESCRIPTION_STEP
|
||||
is_example_project = False
|
||||
|
||||
if 'app_type' not in self.project.args:
|
||||
self.project.args['app_type'] = ask_for_app_type()
|
||||
if 'name' not in self.project.args:
|
||||
while True:
|
||||
question = 'What is the project name?'
|
||||
print(question, type='ipc')
|
||||
print('start an example project', type='button')
|
||||
project_name = ask_user(self.project, question)
|
||||
if len(project_name) <= MAX_PROJECT_NAME_LENGTH:
|
||||
break
|
||||
else:
|
||||
print(f"Hold your horses cowboy! Please, give project NAME with max {MAX_PROJECT_NAME_LENGTH} characters.")
|
||||
|
||||
if project_name.lower() == 'start an example project':
|
||||
is_example_project = True
|
||||
project_name = 'Example Project'
|
||||
|
||||
self.project.args['name'] = clean_filename(project_name)
|
||||
|
||||
self.project.app = save_app(self.project)
|
||||
|
||||
self.project.set_root_path(setup_workspace(self.project.args))
|
||||
|
||||
print(color_green_bold(
|
||||
"GPT Pilot currently works best for web app projects using Node, Express and MongoDB. "
|
||||
"You can use it with other technologies, but you may run into problems "
|
||||
"(eg. React might not work as expected).\n"
|
||||
))
|
||||
|
||||
self.project.main_prompt = ask_for_main_app_definition(self.project)
|
||||
if is_example_project:
|
||||
print(EXAMPLE_PROJECT_DESCRIPTION)
|
||||
self.project.main_prompt = EXAMPLE_PROJECT_DESCRIPTION
|
||||
else:
|
||||
print(color_green_bold(
|
||||
"GPT Pilot currently works best for web app projects using Node, Express and MongoDB. "
|
||||
"You can use it with other technologies, but you may run into problems "
|
||||
"(eg. React might not work as expected).\n"
|
||||
))
|
||||
self.project.main_prompt = ask_for_main_app_definition(self.project)
|
||||
|
||||
print(json.dumps({'open_project': {
|
||||
#'uri': 'file:///' + self.project.root_path.replace('\\', '/'),
|
||||
|
||||
@@ -62,7 +62,6 @@ class TechLead(Agent):
|
||||
def create_feature_plan(self, feature_description):
|
||||
self.save_dev_steps = True
|
||||
self.convo_feature_plan = AgentConvo(self)
|
||||
previous_features = get_features_by_app_id(self.project.args['app_id'])
|
||||
|
||||
llm_response = self.convo_feature_plan.send_message('development/feature_plan.prompt',
|
||||
{
|
||||
@@ -74,9 +73,8 @@ class TechLead(Agent):
|
||||
"architecture": self.project.architecture,
|
||||
"technologies": self.project.system_dependencies + self.project.package_dependencies,
|
||||
"directory_tree": self.project.get_directory_tree(True),
|
||||
"development_tasks": self.project.development_plan,
|
||||
"files": self.project.get_all_coded_files(),
|
||||
"previous_features": previous_features,
|
||||
"previous_features": self.project.previous_features,
|
||||
"feature_description": feature_description,
|
||||
"task_type": 'feature',
|
||||
}, DEVELOPMENT_PLAN)
|
||||
|
||||
@@ -38,6 +38,8 @@ class TechnicalWriter(Agent):
|
||||
"user_tasks": self.project.user_tasks,
|
||||
"directory_tree": self.project.get_directory_tree(True),
|
||||
"files": self.project.get_all_coded_files(),
|
||||
"previous_features": self.project.previous_features,
|
||||
"current_feature": self.project.current_feature,
|
||||
}, GET_DOCUMENTATION_FILE)
|
||||
|
||||
self.project.save_file(llm_response)
|
||||
|
||||
@@ -5,6 +5,7 @@ If the project requirements call out for specific technology, use that. Otherwis
|
||||
Here are the details for the new project:
|
||||
-----------------------------
|
||||
{{ project_details }}
|
||||
{{ features_list }}
|
||||
-----------------------------
|
||||
|
||||
Based on these details, think step by step to design the architecture for the project and choose technologies to use in building it.
|
||||
|
||||
13
pilot/prompts/components/features_list.prompt
Normal file
13
pilot/prompts/components/features_list.prompt
Normal file
@@ -0,0 +1,13 @@
|
||||
{% if previous_features %}
|
||||
Here is the list of features that were previously implemented on top of initial high level description of "{{ name }}":
|
||||
```
|
||||
{% for feature in previous_features %}
|
||||
- {{ loop.index }}. {{ feature['summary'] }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
{% endif %}{% if current_feature %}Here is the feature that you are implementing right now:
|
||||
```
|
||||
{{ current_feature }}
|
||||
```
|
||||
{% endif %}
|
||||
40
pilot/prompts/development/bug_report.prompt
Normal file
40
pilot/prompts/development/bug_report.prompt
Normal file
@@ -0,0 +1,40 @@
|
||||
Here is description of app that you are working on:
|
||||
```
|
||||
{{ app_summary }}
|
||||
```
|
||||
|
||||
{{ files_list }}
|
||||
|
||||
Client wrote this feedback:
|
||||
```
|
||||
{{ user_feedback }}
|
||||
```
|
||||
{% if questions_and_answers|length > 0 %}
|
||||
Here are questions and answers that you already asked the client:
|
||||
```{% for row in questions_and_answers %}
|
||||
Q: {{ row.question }}
|
||||
A: {{ row.answer }}
|
||||
{% endfor %}
|
||||
```{% endif %}
|
||||
|
||||
Your job is to identify if feedback is good enough for you to solve the problem. If not, what information you need to solve the problem. Ask for any information that you need to solve the problem.
|
||||
If you have enough information don't ask any questions.
|
||||
|
||||
When thinking of questions, consider the following:
|
||||
- After getting answers to your questions, you must be able to solve the problem.
|
||||
- Category on which you need more information ("general", "frontend", "backend", "database", "devops", "other")
|
||||
- Client is not very technical person but understands basics. It is crucial that your questions can be answered without looking into code or knowing codebase. Do not ask how something is implemented, how code works or for code snippets.
|
||||
- Make sure to cover all categories that you need to solve the problem.
|
||||
- Ask only specific information that you need to solve the problem.
|
||||
- Ask clear, short and concise questions.
|
||||
- Ask only crucial questions. Do not ask for information that you do not need to solve the problem.
|
||||
- Ask least amount of questions to get the most information.
|
||||
- Ask least amount of questions to solve the problem.
|
||||
|
||||
Here are some examples of good questions:
|
||||
"Are there any logs in browser?"
|
||||
"Can you provide logs from server?"
|
||||
"What is the error message?"
|
||||
"What is the expected behavior?"
|
||||
"What is the actual behavior?"
|
||||
"What is the stack trace?"
|
||||
28
pilot/prompts/development/bug_report_summary.prompt
Normal file
28
pilot/prompts/development/bug_report_summary.prompt
Normal file
@@ -0,0 +1,28 @@
|
||||
You are working on this app:
|
||||
```
|
||||
{{ app_summary }}
|
||||
```
|
||||
|
||||
User came to you with this bug report:
|
||||
```
|
||||
{{ user_feedback }}
|
||||
```
|
||||
You didn't have enough information to start working on it, so you asked the user to provide more details.
|
||||
Here are questions and answers that you asked the user:
|
||||
```{% for row in questions_and_answers %}
|
||||
Q: {{ row.question }}
|
||||
A: {{ row.answer }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Your job is now to write issue explanation that will be sent to developer, strictly following these rules:
|
||||
- If there are some logs you MUST copy all logs in whole! Do not omit any logs! This is also true for code snippets or stack traces!
|
||||
- Explanation must be very clear and factual, keep it as short as possible.
|
||||
- When writing explanation of the issue, it is good to cover all categories that you have information on. If you don't have information about one category, then don't mention it. Here are categories: "general", "frontend", "backend", "database", "devops", "other".
|
||||
- Omit all information that turns out to be irrelevant for this issue (e.g. after asking additional questions it turns out that first user message is not relevant then you should ignore it)
|
||||
- Write issue explanation as if you are talking directly to developer (in first person). Do not mention "user", talk as if you found the the issue.
|
||||
- Do not use any subjective thoughts, just facts.
|
||||
- Write only issue explanation. Do not write any recap, summary or ideas how to solve the problem at the end. Do not write introduction at the start (e.g. "Issue Explanation for Developer:").
|
||||
- Do not write any new code, only if something is provided by user.
|
||||
- Have in mind that developer is smart and he will understand everything as long as you provide all information that you have and that is needed to fix this issue.
|
||||
- Have in mind that issue might not be related to your current development task.
|
||||
@@ -1,19 +1,7 @@
|
||||
You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to add new feature to an existing project. You are working on a {{ app_type }} called "{{ name }}" and you need to create a detailed development plan so that developers can start developing the new feature.
|
||||
|
||||
{{ project_details }}
|
||||
|
||||
Here are development tasks that specify what was already coded:
|
||||
```
|
||||
{{ development_tasks }}
|
||||
```
|
||||
|
||||
{% if previous_features and previous_features|length > 0 %}
|
||||
After those tasks, new features were added to {{ app_type }} ""{{ name }}"". Here is a list of all features that are already implemented:
|
||||
{% for feature in previous_features %}
|
||||
- {{ loop.index }}. {{ feature['summary'] }}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{{ features_list }}
|
||||
Here is directory tree that shows current folder structure of project:
|
||||
```
|
||||
{{ directory_tree }}
|
||||
|
||||
@@ -4,7 +4,7 @@ Here is a high level description of "{{ name }}":
|
||||
```
|
||||
{{ app_summary }}
|
||||
```
|
||||
|
||||
{{ features_list }}
|
||||
Project architecture:
|
||||
{{ architecture }}
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ the full contents of the updated file, without skipping over any content
|
||||
```
|
||||
------------------------end_of_format---------------------------
|
||||
|
||||
**IMPORTANT**:If the instructions have comments like `// ..add code here...` or `# placeholder for code`, instead of copying the comment, interpret the instructions and output the relevant code.
|
||||
|
||||
**IMPORTANT**: Your reply MUST NOT omit any code in the new implementation or substitute anything with comments like `// .. rest of the code goes here ..` or `# insert existing code here`, because I will overwrite the existing file with the content you provide. Output ONLY the content for this file, without additional explanation, suggestions or notes. Your output MUST start with ``` and MUST end with ``` and include only the complete file contents.
|
||||
**IMPORTANT**: If the user must configure something manually, mark the line that needs user configuration with `INPUT_REQUIRED {input_description}` comment, where `input_description` is a description of what needs to be added here by the user. Use appropriate syntax for comments in the file you're saving (for example `// INPUT_REQUIRED {input_description}` in JavaScript). If the file type doesn't support comments (eg JSON), don't add any.
|
||||
|
||||
**IMPORTANT**: For hardcoded configuration values that the user needs to change, mark the line that needs user configuration with `INPUT_REQUIRED {config_description}` comment, where `config_description` is a description of the value that needs to be set by the user. Use appropriate syntax for comments in the file you're saving (for example `// INPUT_REQUIRED {config_description}` in JavaScript). NEVER ask the user to write code or provide implementation, even if the instructions suggest it! If the file type doesn't support comments (eg JSON), don't add any.
|
||||
|
||||
{{ logs_and_error_handling }}
|
||||
|
||||
@@ -4,7 +4,7 @@ Here is a high level description of "{{ name }}":
|
||||
```
|
||||
{{ app_summary }}
|
||||
```
|
||||
|
||||
{{ features_list }}
|
||||
Project architecture:
|
||||
{{ architecture }}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Each step can be either:
|
||||
|
||||
* `command` - command to run (must be able to run on a {{ os }} machine, assume current working directory is project root folder)
|
||||
* `save_file` - create or update ONE file
|
||||
* `human_intervention` - if you need the human to do something, use this type of step and explain in details what you want the human to do. NEVER use `human_intervention` for testing, as testing will be done separately by a dedicated QA after all the steps are done.
|
||||
* `human_intervention` - if you need the human to do something, use this type of step and explain in details what you want the human to do. NEVER use `human_intervention` for testing, as testing will be done separately by a dedicated QA after all the steps are done. Also you MUST NOT use `human_intervention` to ask the human to write or review code.
|
||||
|
||||
**IMPORTANT**: In `code_change_description` field of `save_file` step, you must provide empty string. If multiple changes are required for same file, you must provide single `save_file` step for each file.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to work on a new project. You are working on a {{ app_type }} called "{{ name }}" and you need to create a detailed development plan so that developers can start developing the app.
|
||||
|
||||
{{ project_details }}
|
||||
|
||||
{{ features_list }}
|
||||
{% if files %}The developers have already used a project scaffolding tool that creates the initial boilerplate for the project:
|
||||
{{ existing_summary }}
|
||||
|
||||
|
||||
@@ -15,11 +15,16 @@ Here is the diff of the changes:
|
||||
|
||||
As you can see, there {% if hunks|length == 1 %}is only one hunk in this diff, and it{% else %}are {{hunks|length}} hunks in this diff, and each{% endif %} starts with the `@@` header line.
|
||||
|
||||
Think carefully about the instructions and review the proposed changes. For each hunk of change, provide a detailed rationale, and decide whether it should be applied or should be ignored (for example if it is a code deletion or change that wasn't asked for). Finally, if the changes miss something that was in the instructions, mention that. Keep in mind you're just reviewing one file, `{{ file_name }}`. You don't need to consider if other files are created, dependent packages installed, etc. Focus only on reviewing the changes in this file based on the instructions in the previous message.
|
||||
Think carefully about the instructions and review the proposed changes. For each hunk of change, provide a detailed rationale, and decide whether it should be:
|
||||
* applied - if the change is correct
|
||||
* ignored - for example if it is a code deletion or change that wasn't asked for
|
||||
* reworked - if the change does something correctly but also makes a serious mistake, in which case both applying and ignoring the entire hunk would be incorrect
|
||||
|
||||
Note that the developer may add, modify or delete logging (including `gpt_pilot_debugging_log`) or error handling that's not explicitly asked for, but is a part of good development practice. Unless these logging and error handling additions break something, you should not ignore those changes. Base your decision only on functional changes - comments or logging are less important.
|
||||
Finally, if the changes miss something that was in the instructions, mention that. Keep in mind you're just reviewing one file, `{{ file_name }}`. You don't need to consider if other files are created, dependent packages installed, etc. Focus only on reviewing the changes in this file based on the instructions in the previous message.
|
||||
|
||||
Here is an example output if 3 of 4 hunks in the change should be applied and one of them should be ignored, and no other changes are needed:
|
||||
Note that the developer may add, modify or delete logging (including `gpt_pilot_debugging_log`) or error handling that's not explicitly asked for, but is a part of good development practice. Unless these logging and error handling additions break something, your decision to apply, ignore or rework the hunk should not be based on this. Base your decision only on functional changes - comments or logging are less important. Importantly, don't ask for a rework just because of logging or error handling changes. Also, take into account this is a junior developer and while the approach they take may not be the best practice, if it's not *wrong*, let it pass. Ask for rework only if the change is clearly bad and would break something.
|
||||
|
||||
Here is an example output if 2 of 4 hunks in the change should be applied, one of them should be ignored, one should be reworked, and no other changes are needed:
|
||||
```
|
||||
{
|
||||
"hunks": [
|
||||
@@ -35,13 +40,13 @@ Here is an example output if 3 of 4 hunks in the change should be applied and on
|
||||
},
|
||||
{
|
||||
"number": 3,
|
||||
"reason": "This hunk accidentally deletes important code",
|
||||
"reason": "This hunk accidentally deletes important code without any useful change",
|
||||
"decision": "ignore"
|
||||
},
|
||||
{
|
||||
"number": 4,
|
||||
"reason": "Explanation why the fourth hunk should be included in the change",
|
||||
"decision": "apply"
|
||||
"reason": "This hunk does something correctly but also does something really wrong. It would be incorrect to either apply or ignore it fully, so it should be reworked.",
|
||||
"decision": "rework"
|
||||
},
|
||||
],
|
||||
"review_notes": "General review notes, if something is missing from the change you can comment about it here"
|
||||
|
||||
17
pilot/prompts/development/review_feedback.prompt
Normal file
17
pilot/prompts/development/review_feedback.prompt
Normal file
@@ -0,0 +1,17 @@
|
||||
Your changes have been reviewed.
|
||||
{% if content != original_content %}
|
||||
THe reviewer approved and applied some of your changes, but requested you rework the others.
|
||||
|
||||
Here's the file with the approved changes already applied:
|
||||
```
|
||||
{{ content }}
|
||||
```
|
||||
|
||||
Here's the reviewer's feedback:
|
||||
{% else %}
|
||||
The reviewer requested that you rework your changes, here's the feedback:
|
||||
{% endif %}
|
||||
|
||||
{{ rework_feedback }}
|
||||
|
||||
Based on this feedback and the original instructions, think carefully, make the correct changes, and output the entire file again. Remember, Output ONLY the content for this file, without additional explanation, suggestions or notes. Your output MUST start with ``` and MUST end with ``` and include only the complete file contents.
|
||||
@@ -4,7 +4,7 @@ Here is a high level description of "{{ name }}":
|
||||
```
|
||||
{{ app_summary }}
|
||||
```
|
||||
|
||||
{{ features_list }}
|
||||
Development process of this app was split into smaller tasks. Here is the list of all tasks:
|
||||
```{% for task in tasks %}
|
||||
{{ loop.index }}. {{ task['description'] }}
|
||||
@@ -50,14 +50,15 @@ Now I will show you how those files looked before this task implementation start
|
||||
You have to review this task implementation. You are known to be very strict with your reviews and very good at noticing bugs but you don't mind minor changes like refactoring, adding or removing logs and so on. You think twice through all information given before giving any conclusions.
|
||||
|
||||
Each task goes through multiple reviews and you have to focus only on your part of review.
|
||||
Your goal is to check:
|
||||
In this review, your goal is to check:
|
||||
1. If there are some functionalities that were removed but are still needed.
|
||||
2. If new files or functions are created but never called or used.
|
||||
3. If there is some "dead code" that should be removed.
|
||||
4. If there is some duplicate code resulting from refactoring or moving code into separate classes or files.
|
||||
|
||||
If everything is ok respond only with "DONE" and nothing else. Do NOT respond with thoughts, reasoning, explanations or anything similar if everything is ok, respond just with "DONE".
|
||||
|
||||
If you find any of these 3 mistakes describe shortly what has to be changed.
|
||||
If you find any of these 4 mistakes, describe in detail what has to be changed.
|
||||
|
||||
{{ execution_order }}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
You are working on a {{ app_type }} called "{{ name }}" and you need to write code for the entire {{ task_type }} based on the tasks that the tech lead gives you. So that you understand better what you're working on, you're given other specs for "{{ name }}" as well.
|
||||
|
||||
{{ project_details }}
|
||||
|
||||
{{ features_list }}
|
||||
{{ files_list }}
|
||||
|
||||
We've broken the development of this {{ task_type }} down to these tasks:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
You are working on a {{ app_type }} called "{{ name }}" and you need to create a detailed documentation for current state of project. Your first task is to create README.md file.
|
||||
|
||||
{{ project_details }}
|
||||
|
||||
{{ features_list }}
|
||||
{{ files_list }}
|
||||
|
||||
DO NOT specify commands to create any folders or files, they will be created automatically - just specify the relative path to file that needs to be written.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from utils.style import color_green_bold
|
||||
from logger.logger import logger
|
||||
@@ -51,6 +52,7 @@ def apply_project_template(
|
||||
{
|
||||
"project_name": project_name,
|
||||
"project_description": project_description,
|
||||
"random_secret": uuid4().hex,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
8
pilot/templates/tpl/node_express_mongoose/.env
Normal file
8
pilot/templates/tpl/node_express_mongoose/.env
Normal file
@@ -0,0 +1,8 @@
|
||||
# Port to listen on (example: 3000)
|
||||
PORT=3000
|
||||
|
||||
# MongoDB database URL (example: mongodb://localhost/dbname)
|
||||
DATABASE_URL=mongodb://localhost/myDb # INPUT_REQUIRED {insert your MongoDB url here}
|
||||
|
||||
# Session secret string (must be unique to your server)
|
||||
SESSION_SECRET={{ random_secret }}
|
||||
@@ -36,7 +36,7 @@ mongoose
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Session configuration with connect-mongo
|
||||
// Session configuration with connect-mongo
|
||||
app.use(
|
||||
session({
|
||||
secret: process.env.SESSION_SECRET,
|
||||
@@ -54,13 +54,15 @@ app.on("error", (error) => {
|
||||
// Logging session creation and destruction
|
||||
app.use((req, res, next) => {
|
||||
const sess = req.session;
|
||||
// Make session available to all views
|
||||
res.locals.session = sess;
|
||||
if (!sess.views) {
|
||||
sess.views = 1;
|
||||
console.log("Session created at: ", new Date().toISOString());
|
||||
} else {
|
||||
sess.views++;
|
||||
console.log(
|
||||
`Session accessed again at: ${new Date().toISOString()}, Views: ${sess.views}`,
|
||||
`Session accessed again at: ${new Date().toISOString()}, Views: ${sess.views}, User ID: ${sess.userId || '(unauthenticated)'}`,
|
||||
);
|
||||
}
|
||||
next();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>A3 Time Tracker</title>
|
||||
<title>{{ project_name }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
</head>
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<% if (session && session.userId) { %>
|
||||
<a class="nav-link" href="/auth/logout">Logout</a>
|
||||
<% } else { %>
|
||||
<a class="nav-link" href="/auth/login">Login</a>
|
||||
<% } %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user