From 15279376d40ec59405295af2c80b9c7c96ddd294 Mon Sep 17 00:00:00 2001 From: better629 Date: Wed, 20 Dec 2023 16:01:17 +0800 Subject: [PATCH] rebase update after #589 --- metagpt/actions/action.py | 5 ++--- metagpt/actions/debug_error.py | 10 +++++++--- metagpt/actions/fix_bug.py | 1 + metagpt/actions/run_code.py | 10 +++++++--- metagpt/actions/summarize_code.py | 8 ++++++-- metagpt/actions/write_code.py | 3 +-- metagpt/actions/write_code_review.py | 4 +--- metagpt/roles/qa_engineer.py | 25 ++++++++++++------------- metagpt/roles/role.py | 3 ++- metagpt/schema.py | 4 ++-- metagpt/utils/serialize.py | 13 +++++-------- 11 files changed, 46 insertions(+), 40 deletions(-) diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index e18983d7d..535c25cb9 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -11,12 +11,11 @@ from __future__ import annotations from typing import Optional, Any from pydantic import BaseModel, Field -from metagpt.actions.action_node import ActionNode + from metagpt.llm import LLM from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.schema import CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext - action_subclass_registry = {} @@ -26,7 +25,7 @@ class Action(BaseModel): context: dict | CodingContext | CodeSummarizeContext | TestingContext | RunCodeContext | str | None = "" prefix = "" # aask*时会加上prefix,作为system_message desc = "" # for skill manager - node: ActionNode = Field(default_factory=ActionNode, exclude=True) + # node: ActionNode = Field(default_factory=ActionNode, exclude=True) # builtin variables builtin_class_name: str = "" diff --git a/metagpt/actions/debug_error.py b/metagpt/actions/debug_error.py index 39f3bc1bc..839acdc2e 100644 --- a/metagpt/actions/debug_error.py +++ b/metagpt/actions/debug_error.py @@ -10,11 +10,14 @@ """ import re +from pydantic import Field + from metagpt.actions.action import Action from metagpt.config import CONFIG from metagpt.const import TEST_CODES_FILE_REPO, TEST_OUTPUTS_FILE_REPO +from metagpt.llm import LLM, BaseGPTAPI from metagpt.logs import logger -from metagpt.schema import RunCodeResult +from metagpt.schema import RunCodeResult, RunCodeContext from metagpt.utils.common import CodeParser from metagpt.utils.file_repository import FileRepository @@ -47,8 +50,9 @@ Now you should start rewriting the code: class DebugError(Action): - def __init__(self, name="DebugError", context=None, llm=None): - super().__init__(name, context, llm) + name: str = "DebugError" + context: RunCodeContext = Field(default_factory=RunCodeContext) + llm: BaseGPTAPI = Field(default_factory=LLM) async def run(self, *args, **kwargs) -> str: output_doc = await FileRepository.get_file( diff --git a/metagpt/actions/fix_bug.py b/metagpt/actions/fix_bug.py index 6bd550d3d..eea40c91a 100644 --- a/metagpt/actions/fix_bug.py +++ b/metagpt/actions/fix_bug.py @@ -9,6 +9,7 @@ from metagpt.actions import Action class FixBug(Action): """Fix bug action without any implementation details""" + name: str = "FixBug" async def run(self, *args, **kwargs): raise NotImplementedError diff --git a/metagpt/actions/run_code.py b/metagpt/actions/run_code.py index 1b9fd252f..ea16c8891 100644 --- a/metagpt/actions/run_code.py +++ b/metagpt/actions/run_code.py @@ -18,10 +18,13 @@ import subprocess from typing import Tuple +from pydantic import Field + from metagpt.actions.action import Action from metagpt.config import CONFIG +from metagpt.llm import LLM, BaseGPTAPI from metagpt.logs import logger -from metagpt.schema import RunCodeResult +from metagpt.schema import RunCodeResult, RunCodeContext from metagpt.utils.exceptions import handle_exception PROMPT_TEMPLATE = """ @@ -74,8 +77,9 @@ standard errors: class RunCode(Action): - def __init__(self, name="RunCode", context=None, llm=None): - super().__init__(name, context, llm) + name: str = "RunCode" + context: RunCodeContext = Field(default_factory=RunCodeContext) + llm: BaseGPTAPI = Field(default_factory=LLM) @classmethod @handle_exception diff --git a/metagpt/actions/summarize_code.py b/metagpt/actions/summarize_code.py index f8d8d2b47..0aec15937 100644 --- a/metagpt/actions/summarize_code.py +++ b/metagpt/actions/summarize_code.py @@ -7,12 +7,15 @@ """ from pathlib import Path +from pydantic import Field from tenacity import retry, stop_after_attempt, wait_random_exponential from metagpt.actions.action import Action from metagpt.config import CONFIG from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO +from metagpt.llm import LLM, BaseGPTAPI from metagpt.logs import logger +from metagpt.schema import CodeSummarizeContext from metagpt.utils.file_repository import FileRepository PROMPT_TEMPLATE = """ @@ -89,8 +92,9 @@ flowchart TB class SummarizeCode(Action): - def __init__(self, name="SummarizeCode", context=None, llm=None): - super().__init__(name, context, llm) + name: str = "SummarizeCode" + context: CodeSummarizeContext = Field(default_factory=CodeSummarizeContext) + llm: BaseGPTAPI = Field(default_factory=LLM) @retry(stop=stop_after_attempt(2), wait=wait_random_exponential(min=1, max=60)) async def summarize_code(self, prompt): diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index 046f9f456..4d0690e0f 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -16,7 +16,6 @@ """ import json -from typing import Optional from pydantic import Field from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -90,7 +89,7 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc class WriteCode(Action): name: str = "WriteCode" - context: Optional[Document] = None + context: Document = Field(default_factory=Document) llm: BaseGPTAPI = Field(default_factory=LLM) @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index f4ab0adfe..580069b74 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -8,8 +8,6 @@ WriteCode object, rather than passing them in when calling the run function. """ -from typing import Optional - from pydantic import Field from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -124,7 +122,7 @@ REWRITE_CODE_TEMPLATE = """ class WriteCodeReview(Action): name: str = "WriteCodeReview" - context: Optional[CodingContext] = None + context: CodingContext = Field(default_factory=CodingContext) llm: BaseGPTAPI = Field(default_factory=LLM) @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) diff --git a/metagpt/roles/qa_engineer.py b/metagpt/roles/qa_engineer.py index acb79ab80..893faa9dd 100644 --- a/metagpt/roles/qa_engineer.py +++ b/metagpt/roles/qa_engineer.py @@ -17,6 +17,11 @@ from pydantic import Field +from metagpt.actions import ( + DebugError, + RunCode, + WriteTest, +) from metagpt.actions.summarize_code import SummarizeCode from metagpt.config import CONFIG from metagpt.const import ( @@ -24,11 +29,6 @@ from metagpt.const import ( TEST_CODES_FILE_REPO, TEST_OUTPUTS_FILE_REPO, ) -from metagpt.actions import ( - DebugError, - RunCode, - WriteTest, -) from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Document, Message, RunCodeContext, TestingContext @@ -40,17 +40,16 @@ class QaEngineer(Role): name: str = Field(default="Edward") profile: str = Field(default="QaEngineer") goal: str = "Write comprehensive and robust tests to ensure codes will work as expected without bugs" - constraints: str = "The test code you write should conform to code standard like PEP8, be modular, easy to read and maintain" + constraints: str = "The test code you write should conform to code standard like PEP8, be modular, " \ + "easy to read and maintain" test_round_allowed: int = 5 - def __init__( - self, - **kwargs - ): + def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions( - [WriteTest] - ) # FIXME: a bit hack here, only init one action to circumvent _think() logic, will overwrite _think() in future updates + + # FIXME: a bit hack here, only init one action to circumvent _think() logic, + # will overwrite _think() in future updates + self._init_actions([WriteTest]) self._watch([SummarizeCode, WriteTest, RunCode, DebugError]) self.test_round = 0 diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 0bc129174..4bce64245 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -27,7 +27,8 @@ from typing import Iterable, Set, Type, Any from pydantic import BaseModel, Field -from metagpt.actions.action import Action, ActionOutput, action_subclass_registry +from metagpt.actions import Action, ActionOutput +from metagpt.actions.action import action_subclass_registry from metagpt.actions.action_node import ActionNode from metagpt.actions.add_requirement import UserRequirement from metagpt.const import SERDESER_PATH diff --git a/metagpt/schema.py b/metagpt/schema.py index 327bfd2d1..e5df6fb10 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -113,8 +113,8 @@ class Message(BaseModel): ic = instruct_content mapping = actionoutput_str_to_mapping(ic["mapping"]) - actionoutput_class = import_class("ActionOutput", "metagpt.actions.action_output") - ic_obj = actionoutput_class.create_model_class(class_name=ic["class"], mapping=mapping) + actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import + ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=mapping) ic_new = ic_obj(**ic["value"]) kwargs["instruct_content"] = ic_new diff --git a/metagpt/utils/serialize.py b/metagpt/utils/serialize.py index 7bfd55008..1d90e8de8 100644 --- a/metagpt/utils/serialize.py +++ b/metagpt/utils/serialize.py @@ -6,8 +6,6 @@ import copy import pickle from metagpt.utils.common import import_class -from metagpt.actions.action_node import ActionNode -from metagpt.schema import Message def actionoutout_schema_to_mapping(schema: dict) -> dict: @@ -90,27 +88,26 @@ def serialize_message(message: "Message"): def deserialize_general_message(message_dict: dict) -> "Message": """ deserialize Message, not to load""" instruct_content = message_dict.pop("instruct_content") - cause_by = message_dict.pop("cause_by") message_cls = import_class("Message", "metagpt.schema") message = message_cls(**message_dict) if instruct_content: ic = instruct_content mapping = actionoutput_str_to_mapping(ic["mapping"]) - - actionoutput_class = import_class("ActionOutput", "metagpt.actions.action_output") - ic_obj = actionoutput_class.create_model_class(class_name=ic["class"], mapping=mapping) + actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import + ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=mapping) ic_new = ic_obj(**ic["value"]) message.instruct_content = ic_new return message -def deserialize_message(message_ser: str) -> Message: +def deserialize_message(message_ser: str) -> "Message": message = pickle.loads(message_ser) if message.instruct_content: ic = message.instruct_content - ic_obj = ActionNode.create_model_class(class_name=ic["class"], mapping=ic["mapping"]) + actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import + ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=ic["mapping"]) ic_new = ic_obj(**ic["value"]) message.instruct_content = ic_new