From 57121ef395c2659f8b67be025e7e7fbcd621434e Mon Sep 17 00:00:00 2001 From: better629 Date: Tue, 19 Dec 2023 15:53:14 +0800 Subject: [PATCH] remove useless code and format code --- metagpt/actions/action.py | 16 ---- metagpt/actions/design_api.py | 21 ++--- metagpt/actions/prepare_documents.py | 8 ++ metagpt/actions/project_management.py | 9 +- metagpt/actions/write_prd.py | 2 +- metagpt/actions/write_prd_review.py | 26 ++++-- metagpt/environment.py | 5 +- metagpt/memory/memory.py | 10 -- metagpt/roles/product_manager.py | 8 +- metagpt/roles/project_manager.py | 2 +- metagpt/roles/role.py | 91 +------------------ metagpt/schema.py | 3 +- metagpt/utils/serialize.py | 6 -- .../serialize_deserialize/test_action.py | 8 -- 14 files changed, 50 insertions(+), 165 deletions(-) diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index a21f575ea..570863388 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -71,22 +71,6 @@ class Action(BaseModel): def __repr__(self): return self.__str__() - @classmethod - def ser_class(cls) -> dict: - """ serialize class type""" - return { - "action_class": cls.__name__, - "module_name": cls.__module__ - } - - @classmethod - def deser_class(cls, action_dict: dict): - """ deserialize class type """ - action_class_str = action_dict.pop("action_class") - module_name = action_dict.pop("module_name") - action_class = import_class(action_class_str, module_name) - return action_class - async def _aask(self, prompt: str, system_msgs: Optional[list[str]] = None) -> str: """Append default prefix""" if not system_msgs: diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index a13c5873a..c1778d53f 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -12,17 +12,11 @@ import json from pathlib import Path from typing import Optional + from pydantic import Field from metagpt.actions import Action, ActionOutput from metagpt.actions.design_api_an import DESIGN_API_NODE -from typing import List, Optional, Any - -from pydantic import Field - -from metagpt.actions import Action, ActionOutput -from metagpt.llm import LLM -from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.config import CONFIG from metagpt.const import ( DATA_API_DESIGN_FILE_REPO, @@ -31,12 +25,13 @@ from metagpt.const import ( SYSTEM_DESIGN_FILE_REPO, SYSTEM_DESIGN_PDF_FILE_REPO, ) +from metagpt.llm import LLM from metagpt.logs import logger -from metagpt.schema import Document, Documents +from metagpt.provider.base_gpt_api import BaseGPTAPI +from metagpt.schema import Document, Documents, Message from metagpt.utils.file_repository import FileRepository from metagpt.utils.mermaid import mermaid_to_file - NEW_REQ_TEMPLATE = """ ### Legacy Content {old_design} @@ -50,11 +45,11 @@ class WriteDesign(Action): name: str = "" context: Optional[str] = None llm: BaseGPTAPI = Field(default_factory=LLM) - desc: str = "Based on the PRD, think about the system design, and design the corresponding APIs, " - "data structures, library tables, processes, and paths. Please provide your design, feedback " - "clearly and in detail." + desc: str = "Based on the PRD, think about the system design, and design the corresponding APIs, " \ + "data structures, library tables, processes, and paths. Please provide your design, feedback " \ + "clearly and in detail." - async def run(self, with_messages, format=CONFIG.prompt_format): + async def run(self, with_messages: Message, format: str = CONFIG.prompt_format): # Use `git diff` to identify which PRD documents have been modified in the `docs/prds` directory. prds_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) changed_prds = prds_file_repo.changed_files diff --git a/metagpt/actions/prepare_documents.py b/metagpt/actions/prepare_documents.py index af38b7eae..6bb18be7b 100644 --- a/metagpt/actions/prepare_documents.py +++ b/metagpt/actions/prepare_documents.py @@ -9,16 +9,24 @@ """ import shutil from pathlib import Path +from typing import Optional + +from pydantic import Field from metagpt.actions import Action, ActionOutput from metagpt.config import CONFIG from metagpt.const import DEFAULT_WORKSPACE_ROOT, DOCS_FILE_REPO, REQUIREMENT_FILENAME +from metagpt.llm import LLM +from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.schema import Document from metagpt.utils.file_repository import FileRepository from metagpt.utils.git_repository import GitRepository class PrepareDocuments(Action): + name: str = "PrepareDocuments" + context: Optional[str] = None + llm: BaseGPTAPI = Field(default_factory=LLM) async def run(self, with_messages, **kwargs): if not CONFIG.git_repo: diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index 98a948b64..2727f7e7f 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -11,14 +11,13 @@ """ import json -from typing import List, Optional, Any +from typing import Optional + from pydantic import Field from metagpt.actions import ActionOutput from metagpt.actions.action import Action from metagpt.actions.project_management_an import PM_NODE -from metagpt.llm import LLM -from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.config import CONFIG from metagpt.const import ( PACKAGE_REQUIREMENTS_FILENAME, @@ -26,11 +25,11 @@ from metagpt.const import ( TASK_FILE_REPO, TASK_PDF_FILE_REPO, ) +from metagpt.llm import LLM from metagpt.logs import logger +from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.schema import Document, Documents from metagpt.utils.file_repository import FileRepository -from metagpt.provider.base_gpt_api import BaseGPTAPI - NEW_REQ_TEMPLATE = """ ### Legacy Content diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index e76e91272..f087d8650 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -166,7 +166,7 @@ class WritePRD(Action): if not quadrant_chart: return pathname = ( - CONFIG.git_repo.workdir / Path(COMPETITIVE_ANALYSIS_FILE_REPO) / Path(prd_doc.filename).with_suffix("") + CONFIG.git_repo.workdir / Path(COMPETITIVE_ANALYSIS_FILE_REPO) / Path(prd_doc.filename).with_suffix("") ) if not pathname.parent.exists(): pathname.parent.mkdir(parents=True, exist_ok=True) diff --git a/metagpt/actions/write_prd_review.py b/metagpt/actions/write_prd_review.py index 5ff9624c5..6ed73b6a2 100644 --- a/metagpt/actions/write_prd_review.py +++ b/metagpt/actions/write_prd_review.py @@ -5,20 +5,28 @@ @Author : alexanderwu @File : write_prd_review.py """ + +from typing import Optional + +from pydantic import Field + from metagpt.actions.action import Action +from metagpt.llm import LLM +from metagpt.provider.base_gpt_api import BaseGPTAPI class WritePRDReview(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - self.prd = None - self.desc = "Based on the PRD, conduct a PRD Review, providing clear and detailed feedback" - self.prd_review_prompt_template = """ - Given the following Product Requirement Document (PRD): - {prd} + name: str = "" + context: Optional[str] = None + llm: BaseGPTAPI = Field(default_factory=LLM) + prd: Optional[str] = None + desc: str = "Based on the PRD, conduct a PRD Review, providing clear and detailed feedback" + prd_review_prompt_template: str = """ +Given the following Product Requirement Document (PRD): +{prd} - As a project manager, please review it and provide your feedback and suggestions. - """ +As a project manager, please review it and provide your feedback and suggestions. +""" async def run(self, prd): self.prd = prd diff --git a/metagpt/environment.py b/metagpt/environment.py index 4c8d7d5e5..9108cdf06 100644 --- a/metagpt/environment.py +++ b/metagpt/environment.py @@ -61,6 +61,7 @@ class Environment(BaseModel): "role_class": role.__class__.__name__, "module_name": role.__module__, "role_name": role.name, + "role_sub_tags": list(self.members.get(role)) }) role.serialize(stg_path=stg_path.joinpath(f"roles/{role.__class__.__name__}_{role.name}")) write_json_file(roles_path, roles_info) @@ -70,14 +71,13 @@ class Environment(BaseModel): @classmethod def deserialize(cls, stg_path: Path) -> "Environment": - """ stg_path: ./storage/team/environment/ """ """ stg_path: ./storage/team/environment/ """ roles_path = stg_path.joinpath("roles.json") roles_info = read_json_file(roles_path) roles = [] for role_info in roles_info: # role stored in ./environment/roles/{role_class}_{role_name} - role_path = stg_path.joinpath(f'roles/{role_info.get("role_class")}_{role_info.get("role_name")}') + role_path = stg_path.joinpath(f"roles/{role_info.get('role_class')}_{role_info.get('role_name')}") role = Role.deserialize(role_path) roles.append(role) @@ -96,7 +96,6 @@ class Environment(BaseModel): Add a role in the current environment """ role.set_env(self) - # use alias self.roles[role.profile] = role def add_roles(self, roles: Iterable[Role]): diff --git a/metagpt/memory/memory.py b/metagpt/memory/memory.py index fe70358c9..198c0970d 100644 --- a/metagpt/memory/memory.py +++ b/metagpt/memory/memory.py @@ -20,7 +20,6 @@ from metagpt.utils.utils import read_json_file, write_json_file class Memory(BaseModel): """The most basic memory: super-memory""" - storage: list[Message] = Field(default=[]) index: dict[str, list[Message]] = Field(default_factory=defaultdict(list)) @@ -33,15 +32,6 @@ class Memory(BaseModel): super(Memory, self).__init__(**kwargs) self.index = new_index - def dict(self, *args, **kwargs) -> "DictStrAny": - """ overwrite the `dict` to dump dynamic pydantic model""" - obj_dict = super(Memory, self).dict(*args, **kwargs) - new_obj_dict = copy.deepcopy(obj_dict) - new_obj_dict["index"] = {} - for action_str, value in obj_dict["index"].items(): - new_obj_dict["index"][action_str] = value - return new_obj_dict - def serialize(self, stg_path: Path): """ stg_path = ./storage/team/environment/ or ./storage/team/environment/roles/{role_class}_{role_name}/ """ memory_path = stg_path.joinpath("memory.json") diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index d054b94f5..11bda2127 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -17,7 +17,7 @@ from metagpt.roles.role import Role class ProductManager(Role): """ - Represents a Project Manager role responsible for overseeing project execution and team efficiency. + Represents a Product Manager role responsible for product development and management. Attributes: name (str): Name of the project manager. @@ -28,11 +28,7 @@ class ProductManager(Role): name: str = "Alice" profile: str = Field(default="Product Manager") goal: str = "efficiently create a successful product" - constraints: str = "use same language as user requiremen" - - """ - Represents a Product Manager role responsible for product development and management. - """ + constraints: str = "use same language as user requirement" def __init__(self, **kwargs) -> None: super().__init__(**kwargs) diff --git a/metagpt/roles/project_manager.py b/metagpt/roles/project_manager.py index ec93e609b..f98d28cb7 100644 --- a/metagpt/roles/project_manager.py +++ b/metagpt/roles/project_manager.py @@ -24,7 +24,7 @@ class ProjectManager(Role): """ name: str = Field(default="Eve") profile: str = Field(default="Project Manager") - goal: str = "reak down tasks according to PRD/technical design, generate a task list, and analyze task " \ + goal: str = "break down tasks according to PRD/technical design, generate a task list, and analyze task " \ "dependencies to start with the prerequisite modules" constraints: str = "use same language as user requirement" diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index dbbaf8713..9b1e0bf94 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -81,22 +81,6 @@ class RoleReactMode(str, Enum): return [item.value for item in cls] -class RoleSetting(BaseModel): - """Role Settings""" - name: str = "" - profile: str = "" - goal: str = "" - constraints: str = "" - desc: str = "" - is_human: bool = False - - def __str__(self): - return f"{self.name}({self.profile})" - - def __repr__(self): - return self.__str__() - - class RoleContext(BaseModel): """Role Runtime Context""" # # env exclude=True to avoid `RecursionError: maximum recursion depth exceeded in comparison` @@ -160,7 +144,8 @@ class Role(BaseModel): "_role_id": _role_id, "_states": [], "_actions": [], - "_rc": RoleContext() + "_rc": RoleContext(), + "_subscription": set() } __hash__ = object.__hash__ # support Role as hashable type in `Environment.members` @@ -186,7 +171,7 @@ class Role(BaseModel): # 关于私有变量的初始化 https://github.com/pydantic/pydantic/issues/655 self._private_attributes["_llm"] = LLM() if not self.is_human else HumanProvider() self._private_attributes["_role_id"] = str(self._setting) - self._subscription = {any_to_str(self), name} if name else {any_to_str(self)} + self._private_attributes["_subscription"] = {any_to_str(self), self.name} if self.name else {any_to_str(self)} for key in self._private_attributes.keys(): if key in kwargs: @@ -202,64 +187,7 @@ class Role(BaseModel): else: object.__setattr__(self, key, self._private_attributes[key]) - if not self._rc.watch: - self._watch([UserRequirement]) - - # deserialize child classes dynamically for inherited `role` - object.__setattr__(self, "builtin_class_name", self.__class__.__name__) - self.__fields__["builtin_class_name"].default = self.__class__.__name__ - - def __init_subclass__(cls, **kwargs: Any) -> None: - super().__init_subclass__(**kwargs) - role_subclass_registry[cls.__name__] = cls - - # builtin variables - recovered: bool = False # to tag if a recovered role - builtin_class_name: str = "" - - _private_attributes = { - "_llm": LLM() if not is_human else HumanProvider(), - "_role_id": _role_id, - "_states": [], - "_actions": [], - "_rc": RoleContext() - } - - class Config: - arbitrary_types_allowed = True - exclude = ["_llm"] - - def __init__(self, **kwargs: Any): - for index in range(len(kwargs.get("_actions", []))): - current_action = kwargs["_actions"][index] - if isinstance(current_action, dict): - item_class_name = current_action.get("builtin_class_name", None) - for name, subclass in action_subclass_registry.items(): - registery_class_name = subclass.__fields__["builtin_class_name"].default - if item_class_name == registery_class_name: - current_action = subclass(**current_action) - break - kwargs["_actions"][index] = current_action - - super().__init__(**kwargs) - - # 关于私有变量的初始化 https://github.com/pydantic/pydantic/issues/655 - self._private_attributes["_llm"] = LLM() if not self.is_human else HumanProvider() - self._private_attributes["_role_id"] = str(self._setting) - - for key in self._private_attributes.keys(): - if key in kwargs: - object.__setattr__(self, key, kwargs[key]) - if key == "_rc": - _rc = RoleContext(**kwargs["_rc"]) - object.__setattr__(self, "_rc", _rc) - else: - if key == "_rc": - # # Warning, if use self._private_attributes["_rc"], - # # self._rc will be a shared object between roles, so init one or reset it inside `_reset` - object.__setattr__(self, key, RoleContext()) - else: - object.__setattr__(self, key, self._private_attributes[key]) + self._llm.system_prompt = self._get_prefix() # deserialize child classes dynamically for inherited `role` object.__setattr__(self, "builtin_class_name", self.__class__.__name__) @@ -341,9 +269,6 @@ class Role(BaseModel): self._actions.append(i) self._states.append(f"{idx}. {action}") - def set_react_mode(self, react_mode: RoleReactMode, max_react_loop: int = 1): - self._set_react_mode(react_mode, max_react_loop) - def _set_react_mode(self, react_mode: str, max_react_loop: int = 1): """Set strategy of the Role reacting to observed Message. Variation lies in how this Role elects action to perform during the _think stage, especially if it is capable of multiple Actions. @@ -365,9 +290,6 @@ class Role(BaseModel): if react_mode == RoleReactMode.REACT: self._rc.max_react_loop = max_react_loop - def watch(self, actions: Iterable[Type[Action]]): - self._watch(actions) - def _watch(self, actions: Iterable[Type[Action]]): """Watch Actions of interest. Role will select Messages caused by these Actions from its personal message buffer during _observe. @@ -386,9 +308,6 @@ class Role(BaseModel): if self._rc.env: # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 self._rc.env.set_subscription(self, self._subscription) - def set_state(self, state: int): - self._set_state(state) - def _set_state(self, state: int): """Update the current state.""" self._rc.state = state @@ -436,7 +355,7 @@ class Role(BaseModel): n_states=len(self._states) - 1, previous_state=self._rc.state, ) - # print(prompt) + next_state = await self._llm.aask(prompt) next_state = extract_state_value_from_output(next_state) logger.debug(f"{prompt=}") diff --git a/metagpt/schema.py b/metagpt/schema.py index 690f64128..0ec9b5c60 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -26,6 +26,7 @@ from typing import Dict, List, Set, TypedDict, Optional, Any from pydantic import BaseModel, Field +from metagpt.actions import UserRequirement from metagpt.config import CONFIG from metagpt.const import ( MESSAGE_ROUTE_CAUSE_BY, @@ -118,7 +119,7 @@ class Message(BaseModel): kwargs["instruct_content"] = ic_new kwargs["id"] = uuid.uuid4().hex - kwargs["cause_by"] = any_to_str(kwargs.get("cause_by", "")) + kwargs["cause_by"] = any_to_str(kwargs.get("cause_by", UserRequirement)) kwargs["sent_from"] = any_to_str(kwargs.get("sent_from", "")) kwargs["send_to"] = any_to_str_set(kwargs.get("send_to", {MESSAGE_ROUTE_TO_ALL})) super(Message, self).__init__(**kwargs) diff --git a/metagpt/utils/serialize.py b/metagpt/utils/serialize.py index 9a7049214..93f584057 100644 --- a/metagpt/utils/serialize.py +++ b/metagpt/utils/serialize.py @@ -68,9 +68,6 @@ def serialize_general_message(message: "Message") -> dict: mapping = actionoutput_mapping_to_str(mapping) message_cp.instruct_content = {"class": schema["title"], "mapping": mapping, "value": ic.dict()} - cb = message_cp.cause_by - if cb: - message_cp.cause_by = cb.ser_class() return message_cp.dict() @@ -103,9 +100,6 @@ def deserialize_general_message(message_dict: dict) -> "Message": ic_obj = actionoutput_class.create_model_class(class_name=ic["class"], mapping=mapping) ic_new = ic_obj(**ic["value"]) message.instruct_content = ic_new - if cause_by: - action_class = import_class("Action", "metagpt.actions.action") - message.cause_by = action_class.deser_class(cause_by) return message diff --git a/tests/metagpt/serialize_deserialize/test_action.py b/tests/metagpt/serialize_deserialize/test_action.py index 2db5d223c..63d8e7b7c 100644 --- a/tests/metagpt/serialize_deserialize/test_action.py +++ b/tests/metagpt/serialize_deserialize/test_action.py @@ -25,11 +25,3 @@ async def test_action_deserialize(): assert new_action.name == "" assert new_action.llm == LLM() assert len(await new_action._aask("who are you")) > 0 - - -def test_action_serdeser(): - action_info = WriteTest.ser_class() - assert action_info["action_class"] == "WriteTest" - - action_class = Action.deser_class(action_info) - assert action_class == WriteTest