diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index fd4784f65..46a3bf970 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -54,6 +54,13 @@ Your changes have NOT been applied. Please fix your edit command and try again """ +LINE_NUMBER_AND_CONTENT_MISMATCH = """ +Error: The `{position}_line_number` does not match the `{position}_line_content`. Please correct the parameters. +The `{position}_line_number` is {line_number} and the corresponding content is "{true_content}". +But the `{position}_line_content` is "{fake_content}". +The content around the specified line is: +{context} +""".strip() SUCCESS_EDIT_INFO = """ [File: {file_name} ({n_total_lines} lines total after edit)] {window_after_applied} @@ -756,6 +763,32 @@ class Editor(BaseModel): file_name = self._try_fix_path(file_name) + # Check if the start_line_number and end_line_number correspond to the appropriate content. + mismatch_error = "" + with file_name.open() as file: + content = file.read() + # Ensure the content ends with a newline character + if not content.endswith("\n"): + content += "\n" + lines = content.splitlines(True) + total_lines = len(lines) + check_list = [("start", start_line_number, start_line_content), ("end", end_line_number, end_line_content)] + for position, line_number, line_content in check_list: + if lines[line_number - 1].rstrip() != line_content: + start = max(1, line_number - 3) + end = min(total_lines, line_number + 3) + context = "".join( + [f"{line_number:03d}|{lines[line_number-1]}" for line_number in range(start, end + 1)] + ) + mismatch_error += LINE_NUMBER_AND_CONTENT_MISMATCH.format( + position=position, + line_number=line_number, + true_content=lines[line_number - 1].rstrip(), + fake_content=line_content, + context=context, + ) + if mismatch_error: + return mismatch_error ret_str = self._edit_file_impl( file_name, start=start_line_number, diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index de773f04d..91c74085e 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -537,6 +537,45 @@ 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_number=5, + start_line_content=" b = 2", + new_content=" b = 9", + end_line_number=5, + end_line_content=" b = 2", + ) + with open(temp_py_file, "r") as f: + new_content = f.read() + assert new_content.strip() == EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip() + + +MISMATCH_ERROR = """ +Error: The `start_line_number` does not match the `start_line_content`. Please correct the parameters. +The `start_line_number` is 5 and the corresponding content is " b = 2". +But the `start_line_content` is "". +The content around the specified line is: +002|def test_function_for_fm(): +003| "some docstring" +004| a = 1 +005| b = 2 +006| c = 3 +007| # this is the 7th line +Error: The `end_line_number` does not match the `end_line_content`. Please correct the parameters. +The `end_line_number` is 5 and the corresponding content is " b = 2". +But the `end_line_content` is "". +The content around the specified line is: +002|def test_function_for_fm(): +003| "some docstring" +004| a = 1 +005| b = 2 +006| c = 3 +007| # this is the 7th line +""".strip() + + +def test_edit_file_by_replace_mismatch(temp_py_file): + editor = Editor() + output = editor.edit_file_by_replace( file_name=str(temp_py_file), start_line_number=5, start_line_content="", @@ -544,9 +583,9 @@ def test_edit_file_by_replace(temp_py_file): 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() + with open("tmp.txt", "w", encoding="utf-8") as f: + f.write(output) + assert output.strip() == MISMATCH_ERROR.strip() def test_append_file(temp_file_path):