mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-11 16:52:37 +02:00
Merge branch 'fix/write_new_code_fail' into 'mgx_ops'
fixbug:工程师写新代码重复或输出指令 See merge request pub/MetaGPT!378
This commit is contained in:
commit
b9c74ed819
6 changed files with 49 additions and 23 deletions
|
|
@ -81,10 +81,8 @@ Note:
|
|||
"""
|
||||
CURRENT_STATE = """
|
||||
The current editor state is:
|
||||
(Editor current directory: {editor_current_directory})
|
||||
(Editor open file: {editor_open_file})
|
||||
The current terminal state is:
|
||||
(Terminal current directory: {terminal_current_directory})
|
||||
(Current directory: {current_directory})
|
||||
(Open file: {editor_open_file})
|
||||
"""
|
||||
ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip()
|
||||
|
||||
|
|
@ -104,6 +102,9 @@ WRITE_CODE_PROMPT = """
|
|||
# Plan Status
|
||||
{plan_status}
|
||||
|
||||
# Current Coding File
|
||||
{file_path}
|
||||
|
||||
# Further Instruction
|
||||
{instruction}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,10 +60,13 @@ class Engineer2(RoleZero):
|
|||
Display the current terminal and editor state.
|
||||
This information will be dynamically added to the command prompt.
|
||||
"""
|
||||
current_directory = (await self.terminal.run_command("pwd")).strip()
|
||||
# Synchronize Terminal and Editor Working Directories
|
||||
if str(self.editor.working_dir.absolute()).strip() != current_directory:
|
||||
self.editor._set_workdir(current_directory)
|
||||
state = {
|
||||
"editor_open_file": self.editor.current_file,
|
||||
"editor_current_directory": self.editor.working_dir.absolute(),
|
||||
"terminal_current_directory": (await self.terminal.run_command("pwd")).strip(),
|
||||
"current_directory": current_directory,
|
||||
}
|
||||
self.cmd_prompt_current_state = CURRENT_STATE.format(**state).strip()
|
||||
|
||||
|
|
@ -121,10 +124,14 @@ class Engineer2(RoleZero):
|
|||
plan_status, _ = self._get_plan_status()
|
||||
prompt = WRITE_CODE_PROMPT.format(
|
||||
user_requirement=self.planner.plan.goal,
|
||||
file_path=path,
|
||||
plan_status=plan_status,
|
||||
instruction=instruction,
|
||||
)
|
||||
context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)])
|
||||
# Sometimes the Engineer repeats the last command to respond.
|
||||
# Replace the last command with a manual prompt to guide the Engineer to write new code.
|
||||
memory = self.rc.memory.get(self.memory_k)[:-1]
|
||||
context = self.llm.format_msg(memory + [UserMessage(content=prompt)])
|
||||
|
||||
async with EditorReporter(enable_llm_stream=True) as reporter:
|
||||
await reporter.async_report({"type": "code", "filename": Path(path).name, "src_path": path}, "meta")
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ class RoleZero(Role):
|
|||
for index, cmd in enumerate(commands)
|
||||
if index == index_of_first_exclusive or cmd["command_name"] not in self.exclusive_tool_commands
|
||||
]
|
||||
command_rsp = "```json\n" + json.dumps(commands, indent=4, ensure_ascii=False) + "\n```json"
|
||||
command_rsp = "```json\n" + json.dumps(commands, indent=4, ensure_ascii=False) + "\n```"
|
||||
logger.info(
|
||||
"exclusive command more than one in current command list. change the command list.\n" + command_rsp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -514,6 +514,8 @@ class Editor(BaseModel):
|
|||
temp_file_path = ""
|
||||
src_abs_path = file_name.resolve()
|
||||
first_error_line = None
|
||||
# The file to store previous content and will be removed automatically.
|
||||
temp_backup_file = tempfile.NamedTemporaryFile("w", delete=True)
|
||||
|
||||
try:
|
||||
# lint the original file
|
||||
|
|
@ -556,10 +558,8 @@ class Editor(BaseModel):
|
|||
# because the env var will be set AFTER the agentskills is imported
|
||||
if self.enable_auto_lint:
|
||||
# BACKUP the original file
|
||||
original_file_backup_path = file_name.parent / f".backup.{file_name.name}"
|
||||
with original_file_backup_path.open("w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
temp_backup_file.writelines(lines)
|
||||
temp_backup_file.flush()
|
||||
lint_error, first_error_line = self._lint_file(file_name)
|
||||
|
||||
# Select the errors caused by the modification
|
||||
|
|
@ -609,15 +609,13 @@ class Editor(BaseModel):
|
|||
linter_error_msg=LINTER_ERROR_MSG + lint_error,
|
||||
window_after_applied=self._print_window(file_name, show_line, n_added_lines + 20),
|
||||
window_before_applied=self._print_window(
|
||||
original_file_backup_path, show_line, n_added_lines + 20
|
||||
Path(temp_backup_file.name), show_line, n_added_lines + 20
|
||||
),
|
||||
guidance_message=guidance_message,
|
||||
).strip()
|
||||
|
||||
# recover the original file
|
||||
with original_file_backup_path.open() as fin, file_name.open("w") as fout:
|
||||
fout.write(fin.read())
|
||||
original_file_backup_path.unlink()
|
||||
shutil.move(temp_backup_file.name, src_abs_path)
|
||||
return lint_error_info
|
||||
|
||||
except FileNotFoundError as e:
|
||||
|
|
@ -635,18 +633,16 @@ class Editor(BaseModel):
|
|||
error_info = ERROR_GUIDANCE.format(
|
||||
linter_error_msg=LINTER_ERROR_MSG + str(e),
|
||||
window_after_applied=self._print_window(file_name, start or len(lines), 40),
|
||||
window_before_applied=self._print_window(original_file_backup_path, start or len(lines), 40),
|
||||
window_before_applied=self._print_window(Path(temp_backup_file.name), start or len(lines), 40),
|
||||
guidance_message=guidance_message,
|
||||
).strip()
|
||||
# Clean up the temporary file if an error occurs
|
||||
with original_file_backup_path.open() as fin, file_name.open("w") as fout:
|
||||
fout.write(fin.read())
|
||||
shutil.move(temp_backup_file.name, src_abs_path)
|
||||
if temp_file_path and Path(temp_file_path).exists():
|
||||
Path(temp_file_path).unlink()
|
||||
|
||||
# logger.warning(f"An unexpected error occurred: {e}")
|
||||
raise Exception(f"{error_info}") from e
|
||||
|
||||
# Update the file information and print the updated content
|
||||
with file_name.open("r", encoding="utf-8") as file:
|
||||
n_total_lines = max(1, len(file.readlines()))
|
||||
|
|
@ -798,7 +794,6 @@ class Editor(BaseModel):
|
|||
If you need to use it multiple times, wait for the next turn.
|
||||
"""
|
||||
file_name = self._try_fix_path(file_name)
|
||||
|
||||
ret_str = self._edit_file_impl(
|
||||
file_name,
|
||||
start=line_number,
|
||||
|
|
@ -807,6 +802,7 @@ class Editor(BaseModel):
|
|||
is_insert=True,
|
||||
is_append=False,
|
||||
)
|
||||
self.resource.report(file_name, "path")
|
||||
return ret_str
|
||||
|
||||
def append_file(self, file_name: str, content: str) -> str:
|
||||
|
|
@ -821,7 +817,6 @@ class Editor(BaseModel):
|
|||
If you need to use it multiple times, wait for the next turn.
|
||||
"""
|
||||
file_name = self._try_fix_path(file_name)
|
||||
|
||||
ret_str = self._edit_file_impl(
|
||||
file_name,
|
||||
start=None,
|
||||
|
|
@ -830,6 +825,7 @@ class Editor(BaseModel):
|
|||
is_insert=False,
|
||||
is_append=True,
|
||||
)
|
||||
self.resource.report(file_name, "path")
|
||||
return ret_str
|
||||
|
||||
def search_dir(self, search_term: str, dir_path: str = "./") -> str:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,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.
|
||||
css=self.fake_lint, # base_lint lacks support for css syntax. Use fake_lint to bypass the validation.
|
||||
)
|
||||
self.all_lint_cmd = None
|
||||
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ def test_edit_file_by_replace_to_palce_empty(empty_file):
|
|||
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)
|
||||
assert "is empty. Use the append method to add content." in str(exc_info.value)
|
||||
|
||||
|
||||
def test_append_file(temp_file_path):
|
||||
|
|
@ -604,6 +604,27 @@ def test_search_dir(tmp_path):
|
|||
assert "Another file with different content." not in result
|
||||
|
||||
|
||||
def test_search_dir_in_default_dir(tmp_path):
|
||||
editor = Editor()
|
||||
dir_path = editor.working_dir / "test_dir"
|
||||
dir_path.mkdir(exist_ok=True)
|
||||
|
||||
# Create some files with specific content
|
||||
(dir_path / "file1.txt").write_text("This is a test file with some content.")
|
||||
(dir_path / "file2.txt").write_text("Another file with different content.")
|
||||
sub_dir = dir_path / "sub_dir"
|
||||
sub_dir.mkdir(exist_ok=True)
|
||||
(sub_dir / "file3.txt").write_text("This file is inside a sub directory with some content.")
|
||||
|
||||
search_term = "some content"
|
||||
|
||||
result = editor.search_dir(search_term)
|
||||
|
||||
assert "file1.txt" in result
|
||||
assert "file3.txt" in result
|
||||
assert "Another file with different content." not in result
|
||||
|
||||
|
||||
def test_search_file(temp_file_path):
|
||||
editor = Editor()
|
||||
file_path = temp_file_path
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue