From 2243ea0462608378c76ef2c01a13415e7769962f Mon Sep 17 00:00:00 2001 From: better629 Date: Fri, 29 Dec 2023 13:05:58 +0800 Subject: [PATCH] add extra role/actions' ser&desr unittest --- metagpt/roles/customer_service.py | 9 ++- metagpt/roles/sales.py | 4 +- metagpt/roles/sk_agent.py | 6 +- .../test_prepare_interview.py | 19 +++++++ .../serialize_deserialize/test_reasearcher.py | 22 ++++++++ .../serialize_deserialize/test_sk_agent.py | 24 ++++++++ .../test_tutorial_assistant.py | 20 +++++++ .../test_write_docstring.py | 44 +++++++++++++++ .../test_write_review.py | 56 +++++++++++++++++++ .../test_write_tutorial.py | 43 ++++++++++++++ 10 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 tests/metagpt/serialize_deserialize/test_prepare_interview.py create mode 100644 tests/metagpt/serialize_deserialize/test_reasearcher.py create mode 100644 tests/metagpt/serialize_deserialize/test_sk_agent.py create mode 100644 tests/metagpt/serialize_deserialize/test_tutorial_assistant.py create mode 100644 tests/metagpt/serialize_deserialize/test_write_docstring.py create mode 100644 tests/metagpt/serialize_deserialize/test_write_review.py create mode 100644 tests/metagpt/serialize_deserialize/test_write_tutorial.py diff --git a/metagpt/roles/customer_service.py b/metagpt/roles/customer_service.py index c7baa697d..47f426899 100644 --- a/metagpt/roles/customer_service.py +++ b/metagpt/roles/customer_service.py @@ -7,12 +7,11 @@ """ from typing import Optional +from pydantic import Field + +from metagpt.document_store.base_store import BaseStore from metagpt.roles import Sales -# from metagpt.actions import SearchAndSummarize -# from metagpt.tools import SearchEngineType - - DESC = """ ## Principles (all things must not bypass the principles) @@ -29,4 +28,4 @@ class CustomerService(Sales): name: str = "Xiaomei" profile: str = "Human customer service" desc: str = DESC - store: Optional[str] = None + store: Optional[BaseStore] = Field(default=None, exclude=True) diff --git a/metagpt/roles/sales.py b/metagpt/roles/sales.py index 73075f276..ca1cfee85 100644 --- a/metagpt/roles/sales.py +++ b/metagpt/roles/sales.py @@ -8,6 +8,8 @@ from typing import Optional +from pydantic import Field + from metagpt.actions import SearchAndSummarize, UserRequirement from metagpt.document_store.base_store import BaseStore from metagpt.roles import Role @@ -25,7 +27,7 @@ class Sales(Role): "delivered with the professionalism and courtesy expected of a seasoned sales guide." ) - store: Optional[BaseStore] = None + store: Optional[BaseStore] = Field(default=None, exclude=True) def __init__(self, **kwargs): super().__init__(**kwargs) diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py index f7d229adb..8921774f0 100644 --- a/metagpt/roles/sk_agent.py +++ b/metagpt/roles/sk_agent.py @@ -41,13 +41,13 @@ class SkAgent(Role): goal: str = "Execute task based on passed in task description" constraints: str = "" - plan: Plan = None + plan: Plan = Field(default=None, exclude=True) planner_cls: Any = None planner: Union[BasicPlanner, SequentialPlanner, ActionPlanner] = None llm: BaseLLM = Field(default_factory=LLM) kernel: Kernel = Field(default_factory=Kernel) - import_semantic_skill_from_directory: Callable = None - import_skill: Callable = None + import_semantic_skill_from_directory: Callable = Field(default=None, exclude=True) + import_skill: Callable = Field(default=None, exclude=True) def __init__(self, **data: Any) -> None: """Initializes the Engineer role with given attributes.""" diff --git a/tests/metagpt/serialize_deserialize/test_prepare_interview.py b/tests/metagpt/serialize_deserialize/test_prepare_interview.py new file mode 100644 index 000000000..cd9912103 --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_prepare_interview.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# @Desc : + +import pytest + +from metagpt.actions.action_node import ActionNode +from metagpt.actions.prepare_interview import PrepareInterview + + +@pytest.mark.asyncio +async def test_action_deserialize(): + action = PrepareInterview() + serialized_data = action.model_dump() + assert serialized_data["name"] == "PrepareInterview" + + new_action = PrepareInterview(**serialized_data) + + assert new_action.name == "PrepareInterview" + assert type(await new_action.run("python developer")) == ActionNode diff --git a/tests/metagpt/serialize_deserialize/test_reasearcher.py b/tests/metagpt/serialize_deserialize/test_reasearcher.py new file mode 100644 index 000000000..1b8dbf2c7 --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_reasearcher.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# @Desc : + +import pytest + +from metagpt.actions import CollectLinks +from metagpt.roles.researcher import Researcher + + +@pytest.mark.asyncio +async def test_tutorial_assistant_deserialize(): + role = Researcher() + ser_role_dict = role.model_dump() + assert "name" in ser_role_dict + assert "language" in ser_role_dict + + new_role = Researcher(**ser_role_dict) + assert new_role.language == "en-us" + assert len(new_role.actions) == 3 + assert isinstance(new_role.actions[0], CollectLinks) + + # todo: 需要测试不同的action失败下,记忆是否正常保存 diff --git a/tests/metagpt/serialize_deserialize/test_sk_agent.py b/tests/metagpt/serialize_deserialize/test_sk_agent.py new file mode 100644 index 000000000..7f287b8f9 --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_sk_agent.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# @Desc : +import pytest + +from metagpt.roles.sk_agent import SkAgent + + +def test_sk_agent_serialize(): + role = SkAgent() + ser_role_dict = role.model_dump(exclude={"import_semantic_skill_from_directory", "import_skill"}) + assert "name" in ser_role_dict + assert "planner" in ser_role_dict + + +@pytest.mark.asyncio +async def test_sk_agent_deserialize(): + role = SkAgent() + ser_role_dict = role.model_dump(exclude={"import_semantic_skill_from_directory", "import_skill"}) + assert "name" in ser_role_dict + assert "planner" in ser_role_dict + + new_role = SkAgent(**ser_role_dict) + assert new_role.name == "Sunshine" + assert len(new_role.actions) == 1 diff --git a/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py b/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py new file mode 100644 index 000000000..e642dae54 --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# @Desc : +import pytest + +from metagpt.actions.write_tutorial import WriteDirectory +from metagpt.roles.tutorial_assistant import TutorialAssistant + + +@pytest.mark.asyncio +async def test_tutorial_assistant_deserialize(): + role = TutorialAssistant() + ser_role_dict = role.model_dump() + assert "name" in ser_role_dict + assert "language" in ser_role_dict + assert "topic" in ser_role_dict + + new_role = TutorialAssistant(**ser_role_dict) + assert new_role.name == "Stitch" + assert len(new_role.actions) == 1 + assert isinstance(new_role.actions[0], WriteDirectory) diff --git a/tests/metagpt/serialize_deserialize/test_write_docstring.py b/tests/metagpt/serialize_deserialize/test_write_docstring.py new file mode 100644 index 000000000..89ef6796b --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_write_docstring.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# @Desc : +import pytest + +from metagpt.actions.write_docstring import WriteDocstring + +code = """ +def add_numbers(a: int, b: int): + return a + b + + +class Person: + def __init__(self, name: str, age: int): + self.name = name + self.age = age + + def greet(self): + return f"Hello, my name is {self.name} and I am {self.age} years old." +""" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("style", "part"), + [ + ("google", "Args:"), + ("numpy", "Parameters"), + ("sphinx", ":param name:"), + ], + ids=["google", "numpy", "sphinx"], +) +async def test_action_deserialize(style: str, part: str): + action = WriteDocstring() + serialized_data = action.model_dump() + + assert "name" in serialized_data + assert serialized_data["desc"] == "Write docstring for code." + + new_action = WriteDocstring(**serialized_data) + + assert not new_action.name + assert new_action.desc == "Write docstring for code." + ret = await new_action.run(code, style=style) + assert part in ret diff --git a/tests/metagpt/serialize_deserialize/test_write_review.py b/tests/metagpt/serialize_deserialize/test_write_review.py new file mode 100644 index 000000000..f02a01910 --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_write_review.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# @Desc : +import pytest + +from metagpt.actions.action_node import ActionNode +from metagpt.actions.write_review import WriteReview + +CONTEXT = """ +{ + "Language": "zh_cn", + "Programming Language": "Python", + "Original Requirements": "写一个简单的2048", + "Project Name": "game_2048", + "Product Goals": [ + "创建一个引人入胜的用户体验", + "确保高性能", + "提供可定制的功能" + ], + "User Stories": [ + "作为用户,我希望能够选择不同的难度级别", + "作为玩家,我希望在每局游戏结束后能看到我的得分" + ], + "Competitive Analysis": [ + "Python Snake Game: 界面简单,缺乏高级功能" + ], + "Competitive Quadrant Chart": "quadrantChart\n title \"Reach and engagement of campaigns\"\n x-axis \"Low Reach\" --> \"High Reach\"\n y-axis \"Low Engagement\" --> \"High Engagement\"\n quadrant-1 \"我们应该扩展\"\n quadrant-2 \"需要推广\"\n quadrant-3 \"重新评估\"\n quadrant-4 \"可能需要改进\"\n \"Campaign A\": [0.3, 0.6]\n \"Campaign B\": [0.45, 0.23]\n \"Campaign C\": [0.57, 0.69]\n \"Campaign D\": [0.78, 0.34]\n \"Campaign E\": [0.40, 0.34]\n \"Campaign F\": [0.35, 0.78]\n \"Our Target Product\": [0.5, 0.6]", + "Requirement Analysis": "产品应该用户友好。", + "Requirement Pool": [ + [ + "P0", + "主要代码..." + ], + [ + "P0", + "游戏算法..." + ] + ], + "UI Design draft": "基本功能描述,简单的风格和布局。", + "Anything UNCLEAR": "..." +} +""" + + +@pytest.mark.asyncio +async def test_action_deserialize(): + action = WriteReview() + serialized_data = action.model_dump() + assert serialized_data["name"] == "WriteReview" + + new_action = WriteReview(**serialized_data) + review = await new_action.run(CONTEXT) + + assert new_action.name == "WriteReview" + assert type(review) == ActionNode + assert review.instruct_content + assert review.get("LGTM") in ["LGTM", "LBTM"] diff --git a/tests/metagpt/serialize_deserialize/test_write_tutorial.py b/tests/metagpt/serialize_deserialize/test_write_tutorial.py new file mode 100644 index 000000000..606a90f8c --- /dev/null +++ b/tests/metagpt/serialize_deserialize/test_write_tutorial.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# @Desc : +from typing import Dict + +import pytest + +from metagpt.actions.write_tutorial import WriteContent, WriteDirectory + + +@pytest.mark.asyncio +@pytest.mark.parametrize(("language", "topic"), [("English", "Write a tutorial about Python")]) +async def test_write_directory_deserialize(language: str, topic: str): + action = WriteDirectory() + serialized_data = action.model_dump() + assert serialized_data["name"] == "WriteDirectory" + assert serialized_data["language"] == "Chinese" + + new_action = WriteDirectory(**serialized_data) + ret = await new_action.run(topic=topic) + assert isinstance(ret, dict) + assert "title" in ret + assert "directory" in ret + assert isinstance(ret["directory"], list) + assert len(ret["directory"]) + assert isinstance(ret["directory"][0], dict) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("language", "topic", "directory"), + [("English", "Write a tutorial about Python", {"Introduction": ["What is Python?", "Why learn Python?"]})], +) +async def test_write_content_deserialize(language: str, topic: str, directory: Dict): + action = WriteContent(language=language, directory=directory) + serialized_data = action.model_dump() + assert serialized_data["name"] == "WriteContent" + + new_action = WriteContent(**serialized_data) + ret = await new_action.run(topic=topic) + assert isinstance(ret, str) + assert list(directory.keys())[0] in ret + for value in list(directory.values())[0]: + assert value in ret