From 3fbc85c1b3ba33e12492d6120c75486b80b9319d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 29 Apr 2024 16:55:47 +0800 Subject: [PATCH] feat: Add `UserRequirement` to `Engineer` --- metagpt/ext/stanford_town/roles/st_role.py | 4 +-- metagpt/roles/engineer.py | 34 ++++++++++++---------- metagpt/roles/role.py | 2 +- tests/metagpt/test_environment.py | 12 ++++---- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/metagpt/ext/stanford_town/roles/st_role.py b/metagpt/ext/stanford_town/roles/st_role.py index 79f58b07d..4856548f0 100644 --- a/metagpt/ext/stanford_town/roles/st_role.py +++ b/metagpt/ext/stanford_town/roles/st_role.py @@ -181,13 +181,13 @@ class STRole(Role): logger.info(f"Role: {self.name} saved role's memory into {str(self.role_storage_path)}") - async def _observe(self, ignore_memory=False) -> int: + async def _observe(self) -> int: if not self.rc.env: return 0 news = [] if not news: news = self.rc.msg_buffer.pop_all() - old_messages = [] if ignore_memory else self.rc.memory.get() + old_messages = [] if not self.enable_memory else self.rc.memory.get() # Filter out messages of interest. self.rc.news = [ n for n in news if (n.cause_by in self.rc.watch or self.name in n.send_to) and n not in old_messages diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 5b7c5af24..8959b437a 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -46,6 +46,7 @@ from metagpt.const import ( from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import ( + AIMessage, CodePlanAndChangeContext, CodeSummarizeContext, CodingContext, @@ -60,6 +61,7 @@ from metagpt.utils.common import ( get_project_srcs_path, init_python_folder, ) +from metagpt.utils.git_repository import ChangeType IS_PASS_PROMPT = """ {context} @@ -100,7 +102,7 @@ class Engineer(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - + self.enable_memory = False self.set_actions([WriteCode]) self._watch( [ @@ -148,13 +150,11 @@ class Engineer(Role): dependencies=list(dependencies), content=coding_context.code_doc.content, ) - msg = Message( + AIMessage( content=coding_context.model_dump_json(), instruct_content=coding_context, - role=self.profile, cause_by=WriteCode, ) - self.rc.memory.add(msg) changed_files.add(coding_context.code_doc.filename) if not changed_files: @@ -174,13 +174,12 @@ class Engineer(Role): if isinstance(self.rc.todo, SummarizeCode): self.next_todo_action = any_to_name(WriteCode) return await self._act_summarize() - return None + return await self.rc.todo.run(self.rc.history) async def _act_write_code(self): changed_files = await self._act_sp_with_cr(review=self.use_code_review) - return Message( + return AIMessage( content="\n".join(changed_files), - role=self.profile, cause_by=WriteCodeReview if self.use_code_review else WriteCode, send_to=self, sent_from=self, @@ -213,9 +212,8 @@ class Engineer(Role): logger.info(f"--max-auto-summarize-code={self.config.max_auto_summarize_code}") if not tasks or self.config.max_auto_summarize_code == 0: - return Message( + return AIMessage( content="", - role=self.profile, cause_by=SummarizeCode, sent_from=self, send_to="Edward", # The name of QaEngineer @@ -223,9 +221,7 @@ class Engineer(Role): # The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating unlimited. # This parameter is used for debugging the workflow. self.n_summarize += 1 if self.config.max_auto_summarize_code > self.n_summarize else 0 - return Message( - content=json.dumps(tasks), role=self.profile, cause_by=SummarizeCode, send_to=self, sent_from=self - ) + return AIMessage(content=json.dumps(tasks), cause_by=SummarizeCode, send_to=self, sent_from=self) async def _act_code_plan_and_change(self): """Write code plan and change that guides subsequent WriteCode and WriteCodeReview""" @@ -247,9 +243,8 @@ class Engineer(Role): dependencies=dependencies, ) - return Message( + return AIMessage( content=code_plan_and_change, - role=self.profile, cause_by=WriteCodePlanAndChange, send_to=self, sent_from=self, @@ -270,7 +265,7 @@ class Engineer(Role): self.rc.todo = PrepareDocuments( key_descriptions={ "project_path": 'the project path if exists in "Original Requirement"', - "src_file_path": 'the path of the source code file explicitly requested for modification if exists in "Original Requirement"', + "src_filename": 'the file name of the source code file explicitly requested for modification if exists in "Original Requirement"', }, context=self.context, send_to=any_to_str(self), @@ -337,7 +332,12 @@ class Engineer(Role): async def _new_code_actions(self): bug_fix = await self._is_fixbug() # Prepare file repos - changed_src_files = self.project_repo.srcs.all_files if bug_fix else self.project_repo.srcs.changed_files + changed_src_files = ( + {self.context.kwargs.src_filename: ChangeType.UNTRACTED} + if self.context.kwargs.src_filename + else self.project_repo.srcs.changed_files + ) + changed_src_files = self.project_repo.srcs.all_files if bug_fix else changed_src_files changed_task_files = self.project_repo.docs.task.changed_files changed_files = Documents() # Recode caused by upstream changes. @@ -348,6 +348,8 @@ class Engineer(Role): task_list = self._parse_tasks(task_doc) await self._init_python_folder(task_list) for task_filename in task_list: + if self.context.kwargs.src_filename and task_filename != self.context.kwargs.src_filename: + continue old_code_doc = await self.project_repo.srcs.get(task_filename) if not old_code_doc: old_code_doc = Document( diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 4dc0e34d9..655978f88 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -142,7 +142,7 @@ class Role(SerializationMixin, ContextMixin, BaseModel): desc: str = "" is_human: bool = False enable_memory: bool = ( - True # Stateless, atomic roles, or roles that use external memory can disable this to save memory. + True # Stateless, atomic roles, or roles that use external storage can disable this to save memory. ) role_id: str = "" diff --git a/tests/metagpt/test_environment.py b/tests/metagpt/test_environment.py index 2dd4126e7..15d06d7cd 100644 --- a/tests/metagpt/test_environment.py +++ b/tests/metagpt/test_environment.py @@ -97,14 +97,14 @@ class MockEnv(Environment): # "Add 'random moving enemy, and dispears after 10 seconds' design to the project at '/Users/iorishinier/github/MetaGPT/workspace/snake_game'", # any_to_str(Architect), # ), - ( - 'Rewrite the tasks file of the project at "/Users/iorishinier/github/MetaGPT/workspace/snake_game"', - any_to_str(ProjectManager), - ), # ( - # "Rewrite 'main.py' of the project at '/Users/iorishinier/github/MetaGPT/workspace/snake_game'", - # any_to_str(Engineer), + # 'Rewrite the tasks file of the project at "/Users/iorishinier/github/MetaGPT/workspace/snake_game"', + # any_to_str(ProjectManager), # ), + ( + "Rewrite 'main.py' of the project at '/Users/iorishinier/github/MetaGPT/workspace/snake_game'", + any_to_str(Engineer), + ), # ( # "Rewrite the unit test of 'test_main.py' at '/Users/iorishinier/github/MetaGPT/workspace/snake_game'", # any_to_str(QaEngineer),