From 63a41ba81ded62d306fe9b406dfaaefdc635a6d7 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Tue, 18 Jun 2024 21:35:46 +0800 Subject: [PATCH 01/17] add code_review tool to engineer2 to enhance code effectiveness --- metagpt/actions/di/rewrite_code.py | 80 +++++++++++++++++++++++++ metagpt/prompts/di/engineer2.py | 9 +++ metagpt/roles/di/engineer2.py | 13 +++- tests/metagpt/roles/di/run_engineer2.py | 8 +-- 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 metagpt/actions/di/rewrite_code.py diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py new file mode 100644 index 000000000..a8586d1a6 --- /dev/null +++ b/metagpt/actions/di/rewrite_code.py @@ -0,0 +1,80 @@ +from tenacity import retry, stop_after_attempt, wait_random_exponential + +from metagpt.actions.action import Action +from metagpt.actions.write_code_review import ( + EXAMPLE_AND_INSTRUCTION, + FORMAT_EXAMPLE, + PROMPT_TEMPLATE, + REWRITE_CODE_TEMPLATE, +) +from metagpt.tools.tool_registry import register_tool +from metagpt.utils.common import CodeParser, aread, awrite + + +@register_tool(tags=["RewriteCode"], include_functions=["run"]) +class RewriteCode(Action): + """Accordding design doc and task doc to review the code, to make the complete and correct code.""" + + name: str = "RewriteCode" + + async def run(self, code_path: str, design_doc: str = "", task_doc: str = "", code_review_k_times: int = 2) -> str: + """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. + + Read the code from `code_path`, and write the final code to `code_path`. + + Args: + code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. + design_doc (str): The design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. + task_doc (str): The task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. + code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. + + Returns: + str: The potentially corrected or approved code after review. + + Example Usage: + # Example of how to call the run method with a code snippet and documentation + await WriteCodeReview().run( + code_path="/tmp/game.js", + design_doc='{"Implementation approach":"We will implement the 2048 game using plain JavaScript and HTML, ensuring no frameworks are used. The game logic will handle tile movements, merging, and game state updates. The UI will be simple and clean, with a responsive design to fit different screen sizes. We will use CSS for styling and ensure the game is playable with keyboard arrow keys. The game will display the current score, have a restart button, and show a game over message when no more moves are possible.","File list":["index.html","style.css","script.js"],"Data structures and interfaces":"\nclassDiagram\n class Game {\n -grid: int[][]\n -score: int\n +init(): void\n +move(direction: str): void\n +merge(direction: str): void\n +isGameOver(): bool\n +restart(): void\n }\n class UI {\n -game: Game\n +init(): void\n +update(): void\n +showGameOver(): void\n +bindEvents(): void\n }\n Game --> UI\n","Program call flow":"\nsequenceDiagram\n participant U as UI\n participant G as Game\n U->>G: init()\n G-->>U: return\n U->>U: bindEvents()\n U->>G: move(direction)\n G->>G: merge(direction)\n G->>U: update()\n U->>U: update()\n U->>G: isGameOver()\n G-->>U: return bool\n alt Game Over\n U->>U: showGameOver()\n end\n U->>G: restart()\n G-->>U: return\n","Anything UNCLEAR":"Clarify if there are any specific design preferences or additional features required beyond the basic 2048 game functionality."}', + task_doc='{"Required packages":["No third-party dependencies required"],"Required Other language third-party packages":["No third-party dependencies required"],"Logic Analysis":[["script.js","Contains Game and UI classes, and their methods: init, move, merge, isGameOver, restart, update, showGameOver, bindEvents"],["index.html","Contains the HTML structure for the game UI"],["style.css","Contains the CSS styles for the game UI"]],"Task list":["index.html","style.css","script.js"],"Full API spec":"","Shared Knowledge":"The `script.js` file will contain the core game logic and UI handling. The `index.html` file will provide the structure for the game, and `style.css` will handle the styling.","Anything UNCLEAR":"Clarify if there are any specific design preferences or additional features required beyond the basic 2048 game functionality."}' + ) + """ + code = await aread(code_path) + + context = "\n".join( + [ + "## System Design\n" + design_doc + "\n", + "## Task\n" + task_doc + "\n", + ] + ) + + for _ in range(code_review_k_times): + context_prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=code_path) + cr_prompt = EXAMPLE_AND_INSTRUCTION.format( + format_example=FORMAT_EXAMPLE.format(filename=code_path), + ) + result, rewrited_code = await self.write_code_review_and_rewrite( + context_prompt, cr_prompt, filename=code_path + ) + + if "LBTM" in result: + code = rewrited_code + elif "LGTM" in result: + break + + await awrite(filename=code_path, data=code) + + return code + + @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) + async def write_code_review_and_rewrite(self, context_prompt: str, cr_prompt: str, filename: str): + cr_rsp = await self._aask(context_prompt + cr_prompt) + result = CodeParser.parse_block("Code Review Result", cr_rsp) + if "LGTM" in result: + return result, None + + # if LBTM, rewrite code + rewrite_prompt = f"{context_prompt}\n{cr_rsp}\n{REWRITE_CODE_TEMPLATE.format(filename=filename)}" + code_rsp = await self._aask(rewrite_prompt) + code = CodeParser.parse_code(block="", text=code_rsp) + return result, code diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 4fd52e320..8e2722b49 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -5,5 +5,14 @@ EXTRA_INSTRUCTION = """ 5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response. 6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. 7. When provided system design or project schedule, read them first, then adhere to them in your implementation. +8. Write at most one file per task, do your best to implement THE ONLY ONE FILE. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. +9. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. +10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. +11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. +12. Do not use Editor to find start_line and end_line, just rewrite the file with the all complete code. +13. Revise task is to use RewriteCode.run to correct code. +14. At the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. """ + + ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip() diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index e013ef09e..2720364ef 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,5 +1,6 @@ from __future__ import annotations +from metagpt.actions.di.rewrite_code import RewriteCode from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero @@ -10,4 +11,14 @@ class Engineer2(RoleZero): goal: str = "Take on game, app, and web development" instruction: str = ENGINEER2_INSTRUCTION - tools: str = ["Plan", "Editor:write,read,write_content", "RoleZero"] + tools: str = ["Plan", "Editor:write,read,write_content", "RoleZero", "RewriteCode"] + + def _update_tool_execution(self): + rewrite_code = RewriteCode() + + self.tool_execution_map.update( + { + "RewriteCode.run": rewrite_code.run, + "RewriteCode": rewrite_code.run, + } + ) diff --git a/tests/metagpt/roles/di/run_engineer2.py b/tests/metagpt/roles/di/run_engineer2.py index 4e948bad7..e5ae74485 100644 --- a/tests/metagpt/roles/di/run_engineer2.py +++ b/tests/metagpt/roles/di/run_engineer2.py @@ -67,18 +67,18 @@ Create a 2048 game, follow the design doc and task doc. Write your code under /U After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. Notice: You MUST implement the full code, don't leave comment without implementation! Design doc: -{TASK_DOC_2048} -Task doc: {DESIGN_DOC_2048} +Task doc: +{TASK_DOC_2048} """ GAME_REQ_SNAKE = f""" Create a snake game, follow the design doc and task doc. Write your code under /Users/gary/Files/temp/workspace/snake_game/src. After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. Notice: You MUST implement the full code, don't leave comment without implementation! Design doc: -{TASK_DOC_SNAKE} -Task doc: {DESIGN_DOC_SNAKE} +Task doc: +{TASK_DOC_SNAKE} """ GAME_REQ_2048_NO_DOC = """ Create a 2048 game with pygame. Write your code under /Users/gary/Files/temp/workspace/2048_game/src. From 47df4c09ba2e716e21c5f38047cb79f8af0cd67c Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Wed, 19 Jun 2024 15:42:07 +0800 Subject: [PATCH 02/17] add code_review tool to engineer2 to enhance code effectiveness --- metagpt/actions/di/rewrite_code.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index a8586d1a6..57abbc56c 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -35,8 +35,8 @@ class RewriteCode(Action): # Example of how to call the run method with a code snippet and documentation await WriteCodeReview().run( code_path="/tmp/game.js", - design_doc='{"Implementation approach":"We will implement the 2048 game using plain JavaScript and HTML, ensuring no frameworks are used. The game logic will handle tile movements, merging, and game state updates. The UI will be simple and clean, with a responsive design to fit different screen sizes. We will use CSS for styling and ensure the game is playable with keyboard arrow keys. The game will display the current score, have a restart button, and show a game over message when no more moves are possible.","File list":["index.html","style.css","script.js"],"Data structures and interfaces":"\nclassDiagram\n class Game {\n -grid: int[][]\n -score: int\n +init(): void\n +move(direction: str): void\n +merge(direction: str): void\n +isGameOver(): bool\n +restart(): void\n }\n class UI {\n -game: Game\n +init(): void\n +update(): void\n +showGameOver(): void\n +bindEvents(): void\n }\n Game --> UI\n","Program call flow":"\nsequenceDiagram\n participant U as UI\n participant G as Game\n U->>G: init()\n G-->>U: return\n U->>U: bindEvents()\n U->>G: move(direction)\n G->>G: merge(direction)\n G->>U: update()\n U->>U: update()\n U->>G: isGameOver()\n G-->>U: return bool\n alt Game Over\n U->>U: showGameOver()\n end\n U->>G: restart()\n G-->>U: return\n","Anything UNCLEAR":"Clarify if there are any specific design preferences or additional features required beyond the basic 2048 game functionality."}', - task_doc='{"Required packages":["No third-party dependencies required"],"Required Other language third-party packages":["No third-party dependencies required"],"Logic Analysis":[["script.js","Contains Game and UI classes, and their methods: init, move, merge, isGameOver, restart, update, showGameOver, bindEvents"],["index.html","Contains the HTML structure for the game UI"],["style.css","Contains the CSS styles for the game UI"]],"Task list":["index.html","style.css","script.js"],"Full API spec":"","Shared Knowledge":"The `script.js` file will contain the core game logic and UI handling. The `index.html` file will provide the structure for the game, and `style.css` will handle the styling.","Anything UNCLEAR":"Clarify if there are any specific design preferences or additional features required beyond the basic 2048 game functionality."}' + design_doc='{"Implementation approach":"We will implement the 2048 game..."}', + task_doc='{"Required packages":["No third-party dependencies required"],"..."}' ) """ code = await aread(code_path) From 942a6d61bb73eb9847c7c9b1f6a932964a03751a Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Wed, 19 Jun 2024 23:41:09 +0800 Subject: [PATCH 03/17] opt engineer2 --- metagpt/actions/di/rewrite_code.py | 8 +++++++- metagpt/prompts/di/engineer2.py | 6 +++--- metagpt/prompts/di/team_leader.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 57abbc56c..4ad612002 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -7,6 +7,7 @@ from metagpt.actions.write_code_review import ( PROMPT_TEMPLATE, REWRITE_CODE_TEMPLATE, ) +from metagpt.logs import logger from metagpt.tools.tool_registry import register_tool from metagpt.utils.common import CodeParser, aread, awrite @@ -21,6 +22,7 @@ class RewriteCode(Action): """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. + If there is no `design_doc` or `task_doc`, it will return and do nothing. Args: code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. @@ -39,6 +41,9 @@ class RewriteCode(Action): task_doc='{"Required packages":["No third-party dependencies required"],"..."}' ) """ + if not design_doc or not task_doc: + return + code = await aread(code_path) context = "\n".join( @@ -48,11 +53,12 @@ class RewriteCode(Action): ] ) - for _ in range(code_review_k_times): + for i in range(code_review_k_times): context_prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=code_path) cr_prompt = EXAMPLE_AND_INSTRUCTION.format( format_example=FORMAT_EXAMPLE.format(filename=code_path), ) + logger.info(f"The {i+1}th time to CodeReview: {code_path}.") result, rewrited_code = await self.write_code_review_and_rewrite( context_prompt, cr_prompt, filename=code_path ) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 8e2722b49..55947f3b9 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -9,9 +9,9 @@ EXTRA_INSTRUCTION = """ 9. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. -12. Do not use Editor to find start_line and end_line, just rewrite the file with the all complete code. -13. Revise task is to use RewriteCode.run to correct code. -14. At the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. +12. To modify code in a file, read the entire file, make changes, and use Editor.write instead of Editor.write_content to update the file with the complete code. +13. Revise task is to use RewriteCode.run to correct code, must pass the content of system design and project schedule instead of just file path of them. +14. Only When provided system design, at the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. """ diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 7af5a6e07..f166cee38 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -20,7 +20,7 @@ Note: 3. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly. 4. If the requirement is a common-sense, logical, or math problem, you should respond directly without assigning any task to team members. 5. If you think the requirement is not clear or ambiguous, you should ask the user for clarification immediately. Assign tasks only after all info is clear. -6. It is helpful for Engineer to have both the system design and the project schedule for writing the code, so include paths of both files (if available) when publishing message to Engineer. +6. It is helpful for Engineer to have both the system design and the project schedule for writing the code, so include paths of both files (if available) and remind Engineer to definitely read them when publishing message to Engineer. """ FINISH_CURRENT_TASK_CMD = """ From 599eecf1f460d2795a12d8945dd43de82c33dfa2 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 10:34:20 +0800 Subject: [PATCH 04/17] opt engineer2 --- metagpt/actions/di/rewrite_code.py | 22 ++++++++++++++-------- metagpt/prompts/di/engineer2.py | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 4ad612002..772a9ee99 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -1,3 +1,5 @@ +import asyncio + from tenacity import retry, stop_after_attempt, wait_random_exponential from metagpt.actions.action import Action @@ -18,16 +20,18 @@ class RewriteCode(Action): name: str = "RewriteCode" - async def run(self, code_path: str, design_doc: str = "", task_doc: str = "", code_review_k_times: int = 2) -> str: + async def run( + self, code_path: str, design_doc_path: str = "", task_doc_path: str = "", code_review_k_times: int = 2 + ) -> str: """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. - If there is no `design_doc` or `task_doc`, it will return and do nothing. + If there is no `design_doc_path` or `task_doc_path`, it will return and do nothing. Args: code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. - design_doc (str): The design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. - task_doc (str): The task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. + design_doc_path (str): The file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. + task_doc_path (str): The file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. Returns: @@ -37,14 +41,16 @@ class RewriteCode(Action): # Example of how to call the run method with a code snippet and documentation await WriteCodeReview().run( code_path="/tmp/game.js", - design_doc='{"Implementation approach":"We will implement the 2048 game..."}', - task_doc='{"Required packages":["No third-party dependencies required"],"..."}' + design_doc="/tmp/design_doc.json", + task_doc="/tmp/task_doc.json" ) """ - if not design_doc or not task_doc: + if not design_doc_path or not design_doc_path: return - code = await aread(code_path) + code, design_doc, task_doc = await asyncio.gather( + aread(code_path), aread(design_doc_path), aread(task_doc_path) + ) context = "\n".join( [ diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 55947f3b9..eab8b44ff 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and use Editor.write instead of Editor.write_content to update the file with the complete code. -13. Revise task is to use RewriteCode.run to correct code, must pass the content of system design and project schedule instead of just file path of them. +13. Revise task is to use RewriteCode.run to correct code. 14. Only When provided system design, at the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. """ From 7538e715edd5af8577038cfbe39bfddb9314bbfb Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 10:36:19 +0800 Subject: [PATCH 05/17] opt engineer2 --- metagpt/actions/di/rewrite_code.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 772a9ee99..0b00cd1b5 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -41,8 +41,8 @@ class RewriteCode(Action): # Example of how to call the run method with a code snippet and documentation await WriteCodeReview().run( code_path="/tmp/game.js", - design_doc="/tmp/design_doc.json", - task_doc="/tmp/task_doc.json" + design_doc_path="/tmp/design_doc.json", + task_doc_path="/tmp/task_doc.json" ) """ if not design_doc_path or not design_doc_path: From 0d3187e3089a64ea11db976d4815502ccf6d197d Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 11:32:07 +0800 Subject: [PATCH 06/17] update --- metagpt/actions/di/rewrite_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 0b00cd1b5..03831f912 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -45,7 +45,7 @@ class RewriteCode(Action): task_doc_path="/tmp/task_doc.json" ) """ - if not design_doc_path or not design_doc_path: + if not design_doc_path or not task_doc_path: return code, design_doc, task_doc = await asyncio.gather( From 256e73e878b41ce261bfe8c71bc67f654c76cd48 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 12:29:37 +0800 Subject: [PATCH 07/17] rewrite_code accept content or file_path of design doc --- metagpt/actions/di/rewrite_code.py | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 03831f912..ea093b36c 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -1,4 +1,4 @@ -import asyncio +import os from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -21,17 +21,17 @@ class RewriteCode(Action): name: str = "RewriteCode" async def run( - self, code_path: str, design_doc_path: str = "", task_doc_path: str = "", code_review_k_times: int = 2 + self, code_path: str, design_doc_input: str = "", task_doc_input: str = "", code_review_k_times: int = 2 ) -> str: """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. - If there is no `design_doc_path` or `task_doc_path`, it will return and do nothing. + If there is no `design_doc_input` or `task_doc_input`, it will return and do nothing. Args: code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. - design_doc_path (str): The file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. - task_doc_path (str): The file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. + design_doc_input (str): Content or file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. + task_doc_input (str): Content or file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. Returns: @@ -41,21 +41,28 @@ class RewriteCode(Action): # Example of how to call the run method with a code snippet and documentation await WriteCodeReview().run( code_path="/tmp/game.js", - design_doc_path="/tmp/design_doc.json", - task_doc_path="/tmp/task_doc.json" + design_doc_input="/tmp/design_doc.json", + task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' ) """ - if not design_doc_path or not task_doc_path: + if not design_doc_input or not task_doc_input: return - code, design_doc, task_doc = await asyncio.gather( - aread(code_path), aread(design_doc_path), aread(task_doc_path) - ) + code = await aread(code_path) + + # Check if design_doc_input and task_doc_input are paths or content, and read if they are paths + if os.path.exists(design_doc_input): + logger.info(f"read from {design_doc_input}") + design_doc_input = await aread(design_doc_input) + + if os.path.exists(task_doc_input): + logger.info(f"read from {task_doc_input}") + task_doc_input = await aread(task_doc_input) context = "\n".join( [ - "## System Design\n" + design_doc + "\n", - "## Task\n" + task_doc + "\n", + "## System Design\n" + design_doc_input + "\n", + "## Task\n" + task_doc_input + "\n", ] ) From 76f0d5aad825cd7ba2138150fdc78baa8bc9489f Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 12:31:38 +0800 Subject: [PATCH 08/17] update comment --- metagpt/actions/di/rewrite_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index ea093b36c..504ff72c8 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -39,7 +39,7 @@ class RewriteCode(Action): Example Usage: # Example of how to call the run method with a code snippet and documentation - await WriteCodeReview().run( + await RewriteCode().run( code_path="/tmp/game.js", design_doc_input="/tmp/design_doc.json", task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' From 896c7d8d4cd774b644b0bbddc0c0bcc13bf6c818 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 13:03:51 +0800 Subject: [PATCH 09/17] rewrite_code accept content or file_path of design doc --- metagpt/actions/di/rewrite_code.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 504ff72c8..0b03b534e 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -1,3 +1,4 @@ +import asyncio import os from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -45,24 +46,18 @@ class RewriteCode(Action): task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' ) """ + if not design_doc_input or not task_doc_input: return - code = await aread(code_path) - - # Check if design_doc_input and task_doc_input are paths or content, and read if they are paths - if os.path.exists(design_doc_input): - logger.info(f"read from {design_doc_input}") - design_doc_input = await aread(design_doc_input) - - if os.path.exists(task_doc_input): - logger.info(f"read from {task_doc_input}") - task_doc_input = await aread(task_doc_input) + code, design_doc, task_doc = await asyncio.gather( + aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) + ) context = "\n".join( [ - "## System Design\n" + design_doc_input + "\n", - "## Task\n" + task_doc_input + "\n", + "## System Design\n" + design_doc + "\n", + "## Task\n" + task_doc + "\n", ] ) @@ -97,3 +92,12 @@ class RewriteCode(Action): code_rsp = await self._aask(rewrite_prompt) code = CodeParser.parse_code(block="", text=code_rsp) return result, code + + @staticmethod + async def _try_aread(input: str) -> str: + """Try to read from the path if it's a file; return input directly if not.""" + + if os.path.exists(input): + return await aread(input) + + return input From 6c4cb765c78d1e58c69b6a9bc2a80aa3f515f470 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 14:54:01 +0800 Subject: [PATCH 10/17] reuse write_code_review_and_rewrite --- metagpt/actions/di/rewrite_code.py | 35 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py index 0b03b534e..659635ad5 100644 --- a/metagpt/actions/di/rewrite_code.py +++ b/metagpt/actions/di/rewrite_code.py @@ -1,18 +1,18 @@ import asyncio import os - -from tenacity import retry, stop_after_attempt, wait_random_exponential +from pathlib import Path from metagpt.actions.action import Action from metagpt.actions.write_code_review import ( EXAMPLE_AND_INSTRUCTION, FORMAT_EXAMPLE, PROMPT_TEMPLATE, - REWRITE_CODE_TEMPLATE, + WriteCodeReview, ) from metagpt.logs import logger +from metagpt.schema import CodingContext, Document from metagpt.tools.tool_registry import register_tool -from metagpt.utils.common import CodeParser, aread, awrite +from metagpt.utils.common import aread, awrite @register_tool(tags=["RewriteCode"], include_functions=["run"]) @@ -53,6 +53,8 @@ class RewriteCode(Action): code, design_doc, task_doc = await asyncio.gather( aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) ) + code_doc = self._create_code_doc(code_path=code_path, code=code) + reviewer = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) context = "\n".join( [ @@ -67,8 +69,8 @@ class RewriteCode(Action): format_example=FORMAT_EXAMPLE.format(filename=code_path), ) logger.info(f"The {i+1}th time to CodeReview: {code_path}.") - result, rewrited_code = await self.write_code_review_and_rewrite( - context_prompt, cr_prompt, filename=code_path + result, rewrited_code = await reviewer.write_code_review_and_rewrite( + context_prompt, cr_prompt, doc=code_doc ) if "LBTM" in result: @@ -80,19 +82,6 @@ class RewriteCode(Action): return code - @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) - async def write_code_review_and_rewrite(self, context_prompt: str, cr_prompt: str, filename: str): - cr_rsp = await self._aask(context_prompt + cr_prompt) - result = CodeParser.parse_block("Code Review Result", cr_rsp) - if "LGTM" in result: - return result, None - - # if LBTM, rewrite code - rewrite_prompt = f"{context_prompt}\n{cr_rsp}\n{REWRITE_CODE_TEMPLATE.format(filename=filename)}" - code_rsp = await self._aask(rewrite_prompt) - code = CodeParser.parse_code(block="", text=code_rsp) - return result, code - @staticmethod async def _try_aread(input: str) -> str: """Try to read from the path if it's a file; return input directly if not.""" @@ -101,3 +90,11 @@ class RewriteCode(Action): return await aread(input) return input + + @staticmethod + def _create_code_doc(code_path: str, code: str) -> Document: + """Create a Document to represent the code doc.""" + + path = Path(code_path) + + return Document(root_path=str(path.parent), filename=path.name, content=code) From 050bec2ccfcb23f21ef1170f2df5a6881a927f27 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 15:40:56 +0800 Subject: [PATCH 11/17] update prompt --- metagpt/prompts/di/engineer2.py | 4 ++-- metagpt/roles/di/engineer2.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index eab8b44ff..102792001 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -4,12 +4,12 @@ EXTRA_INSTRUCTION = """ 4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand. 5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response. 6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. -7. When provided system design or project schedule, read them first, then adhere to them in your implementation. +7. When provided system design or project schedule, read them first, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. 8. Write at most one file per task, do your best to implement THE ONLY ONE FILE. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. 9. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. -12. To modify code in a file, read the entire file, make changes, and use Editor.write instead of Editor.write_content to update the file with the complete code. +12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. 13. Revise task is to use RewriteCode.run to correct code. 14. Only When provided system design, at the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. """ diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 2720364ef..152c62ca2 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -11,7 +11,7 @@ class Engineer2(RoleZero): goal: str = "Take on game, app, and web development" instruction: str = ENGINEER2_INSTRUCTION - tools: str = ["Plan", "Editor:write,read,write_content", "RoleZero", "RewriteCode"] + tools: str = ["Plan", "Editor:write,read", "RoleZero", "RewriteCode"] def _update_tool_execution(self): rewrite_code = RewriteCode() From a48ccfdcf9fa7b9ebfaeb69bea7d6675aead4dc0 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Thu, 20 Jun 2024 20:09:25 +0800 Subject: [PATCH 12/17] move RewriteCode to actions/write_code_review.py --- metagpt/actions/di/rewrite_code.py | 100 --------------------------- metagpt/actions/write_code_review.py | 91 +++++++++++++++++++++++- metagpt/prompts/di/engineer2.py | 3 +- metagpt/roles/di/engineer2.py | 2 +- 4 files changed, 92 insertions(+), 104 deletions(-) delete mode 100644 metagpt/actions/di/rewrite_code.py diff --git a/metagpt/actions/di/rewrite_code.py b/metagpt/actions/di/rewrite_code.py deleted file mode 100644 index 659635ad5..000000000 --- a/metagpt/actions/di/rewrite_code.py +++ /dev/null @@ -1,100 +0,0 @@ -import asyncio -import os -from pathlib import Path - -from metagpt.actions.action import Action -from metagpt.actions.write_code_review import ( - EXAMPLE_AND_INSTRUCTION, - FORMAT_EXAMPLE, - PROMPT_TEMPLATE, - WriteCodeReview, -) -from metagpt.logs import logger -from metagpt.schema import CodingContext, Document -from metagpt.tools.tool_registry import register_tool -from metagpt.utils.common import aread, awrite - - -@register_tool(tags=["RewriteCode"], include_functions=["run"]) -class RewriteCode(Action): - """Accordding design doc and task doc to review the code, to make the complete and correct code.""" - - name: str = "RewriteCode" - - async def run( - self, code_path: str, design_doc_input: str = "", task_doc_input: str = "", code_review_k_times: int = 2 - ) -> str: - """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. - - Read the code from `code_path`, and write the final code to `code_path`. - If there is no `design_doc_input` or `task_doc_input`, it will return and do nothing. - - Args: - code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. - design_doc_input (str): Content or file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. - task_doc_input (str): Content or file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. - code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. - - Returns: - str: The potentially corrected or approved code after review. - - Example Usage: - # Example of how to call the run method with a code snippet and documentation - await RewriteCode().run( - code_path="/tmp/game.js", - design_doc_input="/tmp/design_doc.json", - task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' - ) - """ - - if not design_doc_input or not task_doc_input: - return - - code, design_doc, task_doc = await asyncio.gather( - aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) - ) - code_doc = self._create_code_doc(code_path=code_path, code=code) - reviewer = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) - - context = "\n".join( - [ - "## System Design\n" + design_doc + "\n", - "## Task\n" + task_doc + "\n", - ] - ) - - for i in range(code_review_k_times): - context_prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=code_path) - cr_prompt = EXAMPLE_AND_INSTRUCTION.format( - format_example=FORMAT_EXAMPLE.format(filename=code_path), - ) - logger.info(f"The {i+1}th time to CodeReview: {code_path}.") - result, rewrited_code = await reviewer.write_code_review_and_rewrite( - context_prompt, cr_prompt, doc=code_doc - ) - - if "LBTM" in result: - code = rewrited_code - elif "LGTM" in result: - break - - await awrite(filename=code_path, data=code) - - return code - - @staticmethod - async def _try_aread(input: str) -> str: - """Try to read from the path if it's a file; return input directly if not.""" - - if os.path.exists(input): - return await aread(input) - - return input - - @staticmethod - def _create_code_doc(code_path: str, code: str) -> Document: - """Create a Document to represent the code doc.""" - - path = Path(code_path) - - return Document(root_path=str(path.parent), filename=path.name, content=code) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index ad99de2dd..fce29c5bc 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -7,6 +7,9 @@ @Modified By: mashenquan, 2023/11/27. Following the think-act principle, solidify the task parameters when creating the WriteCode object, rather than passing them in when calling the run function. """ +import asyncio +import os +from pathlib import Path from typing import Optional from pydantic import BaseModel, Field @@ -16,7 +19,8 @@ from metagpt.actions import WriteCode from metagpt.actions.action import Action from metagpt.logs import logger from metagpt.schema import CodingContext, Document -from metagpt.utils.common import CodeParser +from metagpt.tools.tool_registry import register_tool +from metagpt.utils.common import CodeParser, aread, awrite from metagpt.utils.project_repo import ProjectRepo from metagpt.utils.report import EditorReporter @@ -205,3 +209,88 @@ class WriteCodeReview(Action): # 如果rewrited_code是None(原code perfect),那么直接返回code self.i_context.code_doc.content = iterative_code return self.i_context + + +@register_tool(tags=["CodeReview"], include_functions=["run"]) +class RewriteCode(Action): + """Accordding design doc and task doc to review the code, to make the complete and correct code.""" + + name: str = "RewriteCode" + + async def run( + self, code_path: str, design_doc_input: str = "", task_doc_input: str = "", code_review_k_times: int = 2 + ) -> str: + """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. + + Read the code from `code_path`, and write the final code to `code_path`. + If there is no `design_doc_input` or `task_doc_input`, it will return and do nothing. + + Args: + code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. + design_doc_input (str): Content or file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. + task_doc_input (str): Content or file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. + code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. + + Returns: + str: The potentially corrected or approved code after review. + + Example Usage: + # Example of how to call the run method with a code snippet and documentation + await RewriteCode().run( + code_path="/tmp/game.js", + design_doc_input="/tmp/design_doc.json", + task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' + ) + """ + + if not design_doc_input or not task_doc_input: + return + + code, design_doc, task_doc = await asyncio.gather( + aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) + ) + code_doc = self._create_code_doc(code_path=code_path, code=code) + reviewer = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) + + context = "\n".join( + [ + "## System Design\n" + design_doc + "\n", + "## Task\n" + task_doc + "\n", + ] + ) + + for i in range(code_review_k_times): + context_prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=code_path) + cr_prompt = EXAMPLE_AND_INSTRUCTION.format( + format_example=FORMAT_EXAMPLE.format(filename=code_path), + ) + logger.info(f"The {i+1}th time to CodeReview: {code_path}.") + result, rewrited_code = await reviewer.write_code_review_and_rewrite( + context_prompt, cr_prompt, doc=code_doc + ) + + if "LBTM" in result: + code = rewrited_code + elif "LGTM" in result: + break + + await awrite(filename=code_path, data=code) + + return code + + @staticmethod + async def _try_aread(input: str) -> str: + """Try to read from the path if it's a file; return input directly if not.""" + + if os.path.exists(input): + return await aread(input) + + return input + + @staticmethod + def _create_code_doc(code_path: str, code: str) -> Document: + """Create a Document to represent the code doc.""" + + path = Path(code_path) + + return Document(root_path=str(path.parent), filename=path.name, content=code) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 102792001..3e8c3723b 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,8 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. -13. Revise task is to use RewriteCode.run to correct code. -14. Only When provided system design, at the end of the plan, add a Revise task for each file; for example, if there are three files, add three Revise tasks. +13. Only with a system design, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks. """ diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 152c62ca2..107101e82 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,6 +1,6 @@ from __future__ import annotations -from metagpt.actions.di.rewrite_code import RewriteCode +from metagpt.actions.write_code_review import RewriteCode from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero From bd3b4dc8c0744a22bdaf38a59b26752caae9f7e9 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Fri, 21 Jun 2024 15:25:00 +0800 Subject: [PATCH 13/17] update action name --- metagpt/actions/write_code_review.py | 23 ++++++++++++----------- metagpt/prompts/di/engineer2.py | 4 ++-- metagpt/roles/di/engineer2.py | 10 +++++----- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index fce29c5bc..62188786d 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -211,11 +211,11 @@ class WriteCodeReview(Action): return self.i_context -@register_tool(tags=["CodeReview"], include_functions=["run"]) -class RewriteCode(Action): - """Accordding design doc and task doc to review the code, to make the complete and correct code.""" +@register_tool(include_functions=["run"]) +class ReviewAndRewriteCode(Action): + """According to the design and task documents, review the code to ensure it is complete and correct.""" - name: str = "RewriteCode" + name: str = "ReviewAndRewriteCode" async def run( self, code_path: str, design_doc_input: str = "", task_doc_input: str = "", code_review_k_times: int = 2 @@ -223,7 +223,7 @@ class RewriteCode(Action): """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. - If there is no `design_doc_input` or `task_doc_input`, it will return and do nothing. + If both `design_doc_input` and `task_doc_input are absent`, it will return and do nothing. Args: code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. @@ -236,21 +236,22 @@ class RewriteCode(Action): Example Usage: # Example of how to call the run method with a code snippet and documentation - await RewriteCode().run( + await ReviewAndRewriteCode().run( code_path="/tmp/game.js", - design_doc_input="/tmp/design_doc.json", - task_doc_input='{"Required packages":["No third-party dependencies required"], ...}' + design_doc_input="/tmp/system_design.json", + task_doc_input="/tmp/project_task_list.json" ) """ - if not design_doc_input or not task_doc_input: + if not design_doc_input and not task_doc_input: + logger.info("Both design_doc_input and task_doc_input are absent, ReviewAndRewriteCode will do nothing.") return code, design_doc, task_doc = await asyncio.gather( aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) ) code_doc = self._create_code_doc(code_path=code_path, code=code) - reviewer = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) + review_action = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) context = "\n".join( [ @@ -265,7 +266,7 @@ class RewriteCode(Action): format_example=FORMAT_EXAMPLE.format(filename=code_path), ) logger.info(f"The {i+1}th time to CodeReview: {code_path}.") - result, rewrited_code = await reviewer.write_code_review_and_rewrite( + result, rewrited_code = await review_action.write_code_review_and_rewrite( context_prompt, cr_prompt, doc=code_doc ) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 3e8c3723b..08259d380 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -4,13 +4,13 @@ EXTRA_INSTRUCTION = """ 4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand. 5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response. 6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. -7. When provided system design or project schedule, read them first, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. +7. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. 8. Write at most one file per task, do your best to implement THE ONLY ONE FILE. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. 9. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. -13. Only with a system design, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks. +13. Only when a system design or a project schedule is provided, at the end of the plan, add a ReviewAndRewriteCode Task for each file; for example, if there are three files, add three ReviewAndRewriteCode Tasks. """ diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 107101e82..845dd8960 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,6 +1,6 @@ from __future__ import annotations -from metagpt.actions.write_code_review import RewriteCode +from metagpt.actions.write_code_review import ReviewAndRewriteCode from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero @@ -11,14 +11,14 @@ class Engineer2(RoleZero): goal: str = "Take on game, app, and web development" instruction: str = ENGINEER2_INSTRUCTION - tools: str = ["Plan", "Editor:write,read", "RoleZero", "RewriteCode"] + tools: str = ["Plan", "Editor:write,read", "RoleZero", "ReviewAndRewriteCode"] def _update_tool_execution(self): - rewrite_code = RewriteCode() + review = ReviewAndRewriteCode() self.tool_execution_map.update( { - "RewriteCode.run": rewrite_code.run, - "RewriteCode": rewrite_code.run, + "ReviewAndRewriteCode.run": review.run, + "ReviewAndRewriteCode": review.run, } ) From 0f65ff5cb07d91a080298a3dafa8091055db4a4b Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Fri, 21 Jun 2024 16:31:10 +0800 Subject: [PATCH 14/17] change parameter names --- metagpt/actions/write_code_review.py | 24 +++++++++++++++--------- metagpt/prompts/di/engineer2.py | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 62188786d..3740a80fe 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -218,17 +218,21 @@ class ReviewAndRewriteCode(Action): name: str = "ReviewAndRewriteCode" async def run( - self, code_path: str, design_doc_input: str = "", task_doc_input: str = "", code_review_k_times: int = 2 + self, + code_path: str, + system_design_input: str = "", + project_schedule_input: str = "", + code_review_k_times: int = 2, ) -> str: """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. - If both `design_doc_input` and `task_doc_input are absent`, it will return and do nothing. + If both `system_design_input` and `project_schedule_input are absent`, it will return and do nothing. Args: code_path (str): The file path of the code snippet to be reviewed. This should be a string containing the path to the source code file. - design_doc_input (str): Content or file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. - task_doc_input (str): Content or file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. + system_design_input (str): Content or file path of the design document associated with the code. This should describe the system architecture, used in the code. It helps provide context for the review process. + project_schedule_input (str): Content or file path of the task document describing what the code is intended to accomplish. This should outline the functional requirements or objectives of the code. code_review_k_times (int, optional): The number of iterations for reviewing and potentially rewriting the code. Defaults to 2. Returns: @@ -238,17 +242,19 @@ class ReviewAndRewriteCode(Action): # Example of how to call the run method with a code snippet and documentation await ReviewAndRewriteCode().run( code_path="/tmp/game.js", - design_doc_input="/tmp/system_design.json", - task_doc_input="/tmp/project_task_list.json" + system_design_input="/tmp/system_design.json", + project_schedule_input="/tmp/project_task_list.json" ) """ - if not design_doc_input and not task_doc_input: - logger.info("Both design_doc_input and task_doc_input are absent, ReviewAndRewriteCode will do nothing.") + if not system_design_input and not project_schedule_input: + logger.info( + "Both `system_design_input` and `project_schedule_input` are absent, ReviewAndRewriteCode will do nothing." + ) return code, design_doc, task_doc = await asyncio.gather( - aread(code_path), self._try_aread(design_doc_input), self._try_aread(task_doc_input) + aread(code_path), self._try_aread(system_design_input), self._try_aread(project_schedule_input) ) code_doc = self._create_code_doc(code_path=code_path, code=code) review_action = WriteCodeReview(i_context=CodingContext(filename=code_doc.filename)) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 08259d380..54b68416a 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. -13. Only when a system design or a project schedule is provided, at the end of the plan, add a ReviewAndRewriteCode Task for each file; for example, if there are three files, add three ReviewAndRewriteCode Tasks. +13. If a system design or project schedule is provided, at the end of the plan, add a ReviewAndRewriteCode Task for each file; for example, if there are three files, add three ReviewAndRewriteCode Tasks. """ From 17e606e1c313aa9aa56548c16075115c219b41f5 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Fri, 21 Jun 2024 18:13:47 +0800 Subject: [PATCH 15/17] update prompt --- metagpt/prompts/di/engineer2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 54b68416a..8a7a48cc2 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. -13. If a system design or project schedule is provided, at the end of the plan, add a ReviewAndRewriteCode Task for each file; for example, if there are three files, add three ReviewAndRewriteCode Tasks. +13. If a system design or project schedule is provided, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks; Each CodeReview Task should execute ReviewAndRewriteCode.run to review and update the code. """ From 7f732799698acc0f7727bde75a359df083d0ec6e Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Fri, 21 Jun 2024 19:56:16 +0800 Subject: [PATCH 16/17] update prompt --- metagpt/prompts/di/engineer2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 8a7a48cc2..5caa5e1a9 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -9,8 +9,8 @@ EXTRA_INSTRUCTION = """ 9. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. -12. To modify code in a file, read the entire file, make changes, and update the file with the complete code. -13. If a system design or project schedule is provided, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks; Each CodeReview Task should execute ReviewAndRewriteCode.run to review and update the code. +12. To modify code in a file, read the entire file, make changes, and update the file with the complete code, ensuring that no line numbers are included in the final write. +13. Only if a system design or project schedule is provided, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks; the CodeReview Task involves using ReviewAndRewriteCode to correct the code. """ From 9454c4dfd644507fa771cd83e266f9c1485f0864 Mon Sep 17 00:00:00 2001 From: hongjiongteng Date: Sun, 23 Jun 2024 23:01:25 +0800 Subject: [PATCH 17/17] update prompt --- metagpt/actions/write_code_review.py | 2 +- metagpt/prompts/di/engineer2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 3740a80fe..e72fe5cd1 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -224,7 +224,7 @@ class ReviewAndRewriteCode(Action): project_schedule_input: str = "", code_review_k_times: int = 2, ) -> str: - """Reviews the provided code based on the accompanying design and task documentation, return the complete and correct code. + """Reviews the provided code based on the accompanying system design and project schedule documentation, return the complete and correct code. Read the code from `code_path`, and write the final code to `code_path`. If both `system_design_input` and `project_schedule_input are absent`, it will return and do nothing. diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 5caa5e1a9..4ac386b80 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code, ensuring that no line numbers are included in the final write. -13. Only if a system design or project schedule is provided, at the end of the plan, add a CodeReview Task for each file; for example, if there are three files, add three CodeReview Tasks; the CodeReview Task involves using ReviewAndRewriteCode to correct the code. +13. When a system design or project schedule is provided, at the end of the plan, add a Special Task for each file; for example, if there are three files, add three Special Tasks. For each Special Task, just call ReviewAndRewriteCode.run. """