diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index db8d2aa5f..b82bff2c2 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -79,6 +79,7 @@ class RoleZero(Role): "Editor.edit_file_by_replace", "Editor.insert_content_at_line", "Editor.append_file", + "Editor.open_file", ] # Equipped with three basic tools by default for optional use editor: Editor = Editor(enable_auto_lint=True) diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 4448c3d02..f8bf7135e 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -13,7 +13,6 @@ from typing import List, Optional, Union from pydantic import BaseModel, ConfigDict from metagpt.const import DEFAULT_WORKSPACE_ROOT -from metagpt.logs import logger from metagpt.tools.libs.index_repo import IndexRepo from metagpt.tools.libs.linter import Linter from metagpt.tools.tool_registry import register_tool @@ -717,8 +716,6 @@ class Editor(BaseModel): If you need to use it multiple times, wait for the next turn. """ # FIXME: support replacing *all* occurrences - if to_replace.strip() == "": - raise ValueError("`to_replace` must not be empty.") if to_replace == new_content: raise ValueError("`to_replace` and `new_content` must be different.") @@ -730,6 +727,12 @@ class Editor(BaseModel): with file_name.open("r") as file: file_content = file.read() + if to_replace.strip() == "": + if file_content.strip() == "": + raise ValueError(f"The file '{file_name}' is empty. Please use the append method to add content.") + else: + raise ValueError("`to_replace` must not be empty.") + if file_content.count(to_replace) > 1: raise ValueError( "`to_replace` appears more than once, please include enough lines to make code in `to_replace` unique." diff --git a/metagpt/tools/libs/linter.py b/metagpt/tools/libs/linter.py index c8760a53b..4e1a2defe 100644 --- a/metagpt/tools/libs/linter.py +++ b/metagpt/tools/libs/linter.py @@ -32,6 +32,7 @@ class Linter: self.languages = dict( python=self.py_lint, + sql=self.fake_lint, # Base_lint lacks support for full SQL syntax. Use fake_lint to bypass the validation. ) self.all_lint_cmd = None @@ -112,6 +113,9 @@ class Linter: error = basic_lint(rel_fname, code) return error + def fake_lint(self, fname, rel_fname, code): + return None + def lint_python_compile(fname, code): try: diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index c601ee5a4..0c5d64a9a 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -45,6 +45,15 @@ def temp_py_file(tmp_path): temp_file_path.unlink() +@pytest.fixture +def empty_file(tmp_path): + assert tmp_path is not None + temp_file_path = tmp_path / "test_script_empty_file_for_editor.py" + temp_file_path.write_text("") + yield temp_file_path + temp_file_path.unlink() + + EXPECTED_CONTENT_AFTER_REPLACE = """ # this is line one def test_function_for_fm(): @@ -530,6 +539,15 @@ def test_edit_file_by_replace(temp_py_file): assert new_content.strip() == EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip() +def test_edit_file_by_replace_to_palce_empty(empty_file): + editor = Editor() + with pytest.raises(ValueError) as exc_info: + editor.edit_file_by_replace( + file_name=str(empty_file), to_replace="", new_content=EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip() + ) + assert "is empty. Use the append method to add content." in str(exc_info.value) + + def test_append_file(temp_file_path): editor = Editor() # 写入初始内容