diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 3ed9389a6..820466b6f 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -69,6 +69,7 @@ CMD_PROMPT = ( you must respond in {respond_language}. Pay close attention to the Example provided, you can reuse the example for your current situation if it fits. +If you open a file, the line number is displayed at the front of each line. You may use any of the available commands to create a plan or update the plan. You may output mutiple commands, they will be executed sequentially. If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_task, DON'T append a new task. Review the latest plan's outcome, focusing on achievements. If your completed task matches the current, consider it finished. diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index eb535fb94..266461f25 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -956,14 +956,15 @@ I have opened the openai_api.py file. However, the range of lines shown is from ``` ## example 9 -I've found the bug and will start fixing it. I'll pay close attention to the indentation. +I want to change the key bindings from (w/s) to the arrow keys (up, down). And add the space bar to pause. the previous file look like: -739| can_fold_brackets = self._settings['fold_func_brackets'] and \ -740| len(args) == 1 and \ -741| not self._needs_function_brackets(expr.args[0]) -742| inv_trig_table = ["asin", "acos", "atan", "acsc", "asec", "acot"] -743| # If the function is an inverse trig function, handle the style -Since I only need to modify a few lines in this file, I will use Editor.edit_file_by_replace. The original content will be replaced by the new code. +142| while not self.is_game_over(): +143| if event.key == pygame.K_w: +144| self.move_up() +145| elif event.key == pygame.K_s: +146| self.move_down() +147| self.add_random_tile() +Since I only need to modify the lines 143 to 146, I will use Editor.edit_file_by_replace. The original content will be replaced by the new code. Editor tool is exclusive. If I use this tool, I cannot use any other commands in the current response. ```json [ @@ -971,46 +972,47 @@ Editor tool is exclusive. If I use this tool, I cannot use any other commands in "command_name": "Editor.edit_file_by_replace", "args": { "file_name":"/workspace/MetaGPT/provider/openai_api.py", - "start_line": 742, - "end_line": 742, - "new_content": " inv_trig_table = ["asin", "acos", "atan", "acsc", "asec", "acot"]" - } + "start_line_number": 143, + "start_line_content":" if event.key == pygame.K_w:", + "new_content": " if event.key == pygame.K_UP:\\n self.move_up()\\n elif event.key == pygame.K_DOWN:\\n self.move_down()\\n elif event.key == pygame.K_SPACE:\\n self.stop()" + "end_line_number": 146, + "end_line_content": " self.move_down()", + } } ] ``` ## example 10 +I want to add a score variable in the initialization of the game. +the previous file look like: +028| if restart: +029| self.snake = Snake() +030| self.food = Food(self.board_size) +031| self.start_game() +032| self.location = (0,0) I only need to add a few lines to the file, so I will use Editor.insert_content_at_line. The new code will not cover the original code. Note that the Editor command must be executed in a single response, so this step will only involve using the Editor command. + ```json [ { "command_name": "Editor.insert_content_at_line", "args": { "file_name":"/workspace/MetaGPT/provider/openai_api.py" - "line_number":727, - "content": "if hasattr(self, '_print_' + func) and not isinstance(expr.func, UndefinedFunction):\\n return getattr(self, '_print_' + func)(expr, exp)" - } - } -] -``` + "line_number":31, + "insert_content": " self.score = Score()" -## example 10.1 -To enhance the functionality of the 2048 game, including game end detection and score tracking, we need to add these features to the existing game_2048.py file. First, we will add a score tracking feature, and then we will insert game end detection logic into the game loop. -We will use the Editor.insert_content_at_line command to insert new code into the file for adding score tracking and game end detection. -Since Editor.insert_content_at_line can only be used once per response, this time I will use it to create the variable self.score -```json -[ - { - "command_name": "Editor.insert_content_at_line", - "args": { - "file_name": "/home/mgx/mgx/MetaGPT/workspace/2048_game_py/game_2048.py", - "line_number": 4, - "content": " self.score = 0\n" } } ] ``` +After executing the command, the file will be: +028| if restart: +029| self.snake = Snake() +030| self.food = Food(self.board_size) +031| self.score = Score() +032| self.start_game() +033| self.location = (0,0) In the next turn, I will try to add another code snippet ## example 11 diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 834615ab9..75f6f2760 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -679,63 +679,78 @@ class Editor(BaseModel): ).strip() return success_edit_info - def edit_file_by_replace(self, file_name: str, start_line: int, end_line: int, new_content: str) -> str: + def edit_file_by_replace( + self, + file_name: str, + start_line_number: int, + start_line_content: str, + end_line_number: int, + end_line_content: str, + new_content: str, + ) -> str: """ Line numbers start from 1. Replaces lines start_line through end_line (inclusive) with the given text in the open file. All of the new_content will be entered, so makesure your indentation is formatted properly. + The new_content must be a complete block of code . Example 1: Given a file "/workspace/example.txt" with the following content: ``` - 001|line 1 - 002|line 2 - 003|line 3 - 004|line 4 + 001|contain f + 002|contain g + 003|contain h + 004|contain i ``` EDITING: If you want to replace line 2 and line 3 edit_file_by_replace( '/workspace/example.txt', - start_line=2, - end_line=3, - new_content='new line', + start_line_number=2, + start_line_content="contain g", + end_line_number=3, + end_line_content="contain h", + new_content='new content', ) - This will replace only the second line 2 and line 3 with "new line". + This will replace only the second line 2 and line 3 with "new content". The resulting file will be: ``` - 001|line 1 - 002|new line - 003|line 4 + 001|contain f + 002|new content + 003|contain i ``` Example 2: Given a file "/workspace/example.txt" with the following content: ``` - 001|line 1 - 002|line 2 - 003|line 3 - 004|line 4 + 001|contain f + 002|contain g + 003|contain h + 004|contain i ``` EDITING: If you want to remove the line 2 and line 3 edit_file_by_replace( '/workspace/example.txt', - start_line=2, - end_line=3, + start_line_number=2, + start_line_content="contain g", + end_line_number=3, + end_line_content="contain h", new_content='new line', ) This will remove line 2 and line 3 The resulting file will be: ``` - 001|line 1 + 001|contain f 002| - 003|line 4 + 003|contain i ``` Args: file_name str:The name of the file to edit. - start_line int: The line number to start the edit at, starting from 1. - end_line int: The line number to end the edit at (inclusive), starting from 1. - new_content str: The text to replace the current selection with, must conform to PEP8 standards. + start_line_number int:The line number to start the edit at, starting from 1. + start_line_content str:The content of the start replace line, according to the start_line_number. + end_line_number int:The line number to end the edit at (inclusive), starting from 1. + end_line_content str:The content of the end replace line, according to the end_line_number. + new_content str: The text to replace the current selection with, must conform to PEP8 standards.The content in the start line and end line will also be replaced. """ @@ -743,8 +758,8 @@ class Editor(BaseModel): ret_str = self._edit_file_impl( file_name, - start=start_line, - end=end_line, + start=start_line_number, + end=end_line_number, content=new_content, ) # TODO: automatically tries to fix linter error (maybe involve some static analysis tools on the location near the edit to figure out indentation) @@ -855,30 +870,36 @@ class Editor(BaseModel): self.resource.report(file_name, "path") return ret_str - def insert_content_at_line(self, file_name: str, line_number: int, content: str) -> str: - """Insert content at the given line number in a file. That is, the new content will start at line_number after the insertion. - This will NOT modify the content of the lines before OR after the given line number. - + def insert_content_at_line(self, file_name: str, line_number: int, insert_content: str) -> str: + """Insert a complete block of code before the given line number in a file That is, the new content will start at the beginning of the specified line, and the existing content of that line will be moved down. + This operation will NOT modify the content of the lines before or after the given line number. + This function can not insert content the end of the file. Please use append_file instead, For example, if the file has the following content: ``` - 001|line 1 - 002|line 2 - 003|line 3 - 004|line 4 + 001|contain g + 002|contain h + 003|contain i + 004|contain j ``` - and you call `insert_content_at_line('file.txt', 2, 'new line')`, the file will be updated to: + and you call + insert_content_at_line( + file_name='file.txt', + line_number=2, + insert_content='new line')` + the file will be updated to: ``` - 001|line 1 + 001|contain g 002|new line - 003|line 2 - 004|line 3 - 005|line 4 + 003|contain h + 004|contain i + 005|contain j ``` Args: file_name: str: The name of the file to edit. - line_number: int: The line number (starting from 1) to insert the content after. - content: str: The content to insert. + line_number int: The line number (starting from 1) to insert the content after.the insert content will be add between the line of line_number-1 and line_number + insert_content (str): The content to insert betweed the previous_line_content and current_line_content.The insert_content must be a complete block of code at. + NOTE: This tool is exclusive. If you use this tool, you cannot use any other commands in the current response. If you need to use it multiple times, wait for the next turn. @@ -888,7 +909,7 @@ class Editor(BaseModel): file_name, start=line_number, end=line_number, - content=content, + content=insert_content, is_insert=True, is_append=False, ) diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index 07efd8132..de773f04d 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -120,7 +120,7 @@ def test_insert_content(temp_py_file): editor.insert_content_at_line( file_name=temp_py_file, line_number=3, - content=" # This is the new line to be inserted, at line 3", + insert_content=" # This is the new line to be inserted, at line 3", ) with open(temp_py_file, "r") as f: new_content = f.read() @@ -536,7 +536,14 @@ def test_function_for_fm(): def test_edit_file_by_replace(temp_py_file): editor = Editor() - editor.edit_file_by_replace(file_name=str(temp_py_file), start_line=5, end_line=5, new_content=" b = 9") + editor.edit_file_by_replace( + file_name=str(temp_py_file), + start_line_number=5, + start_line_content="", + new_content=" b = 9", + end_line_number=5, + end_line_content="", + ) with open(temp_py_file, "r") as f: new_content = f.read() assert new_content.strip() == EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip()