diff --git a/examples/fork_meta_role_write_teaching_plan.py b/examples/fork_meta_role_write_teaching_plan.py deleted file mode 100644 index e529a9b46..000000000 --- a/examples/fork_meta_role_write_teaching_plan.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/7 -@Author : mashenquan -@File : fork_meta_role.py -@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to possess the - ability to construct flows freely by concatenating symbols. Simultaneously, I am also striving to make - these symbols configurable and standardized, making the process of building flow structures more - convenient. This is a fork meta-role demo that implements the functionality of - `examples/write_teaching_plan.py`. -""" - -import asyncio -from pathlib import Path -import sys - -sys.path.append(str(Path(__file__).resolve().parent.parent)) -import aiofiles -import fire -import yaml - -from metagpt.actions.meta_action import MetaAction -from metagpt.logs import logger -from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory -from metagpt.roles.uml_meta_role_options import ProjectConfig -from metagpt.software_company import SoftwareCompany - - -async def startup(lesson_file: str, investment: float = 3.0, n_round: int = 1, *args, **kwargs): - """Run a startup. Be a teacher in education industry.""" - - demo_lesson = """ - UNIT 1 Making New Friends - TOPIC 1 Welcome to China! - Section A - - 1a Listen and number the following names. - Jane Mari Kangkang Michael - Look, listen and understand. Then practice the conversation. - Work in groups. Introduce yourself using - I ’m ... Then practice 1a - with your own hometown or the following places. - - 1b Listen and number the following names - Jane Michael Maria Kangkang - 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places. - China the USA the UK Hong Kong Beijing - - 2a Look, listen and understand. Then practice the conversation - Hello! - Hello! - Hello! - Hello! Are you Maria? - No, I’m not. I’m Jane. - Oh, nice to meet you, Jane - Nice to meet you, too. - Hi, Maria! - Hi, Kangkang! - Welcome to China! - Thanks. - - 2b Work in groups. Make up a conversation with your own name and the - following structures. - A: Hello! / Good morning! / Hi! I’m ... Are you ... ? - B: ... - - 3a Listen, say and trace - Aa Bb Cc Dd Ee Ff Gg - - 3b Listen and number the following letters. Then circle the letters with the same sound as Bb. - Aa Bb Cc Dd Ee Ff Gg - - 3c Match the big letters with the small ones. Then write them on the lines. - """ - - lesson = "" - if lesson_file and Path(lesson_file).exists(): - async with aiofiles.open(lesson_file, mode="r", encoding="utf-8") as reader: - lesson = await reader.read() - logger.info(f"Course content: {lesson}") - if not lesson: - logger.info("No course content provided, using the demo course.") - lesson = demo_lesson - - yaml_filename = kwargs["config"] - kwargs["lesson"] = lesson - - with open(yaml_filename, "r") as reader: - configs = yaml.safe_load(reader) - - startup_config = ProjectConfig(**configs) - company = SoftwareCompany() - roles = UMLMetaRoleFactory.create_roles(role_configs=startup_config.roles, - options=company.options, - cost_manager=company.cost_manager, - **kwargs) - company.hire(roles) - company.invest(startup_config.startup.investment) - company.start_project(lesson, role=startup_config.startup.role, - cause_by=MetaAction.get_action_type(startup_config.startup.requirement)) - await company.run(n_round=startup_config.startup.n_round) - - -def main(idea: str, investment: float = 3.0, n_round: int = 5, *args, **kwargs): - """ - We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities. - :param idea: lesson filename. - :param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company. - :param n_round: Reserved. - :param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3` - :param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2` - :return: - """ - asyncio.run(startup(idea, investment, n_round, *args, **kwargs)) - - -if __name__ == '__main__': - """ - Formats: - ``` - python write_teaching_plan.py lesson_filename --teaching_language= --language= - ``` - If `lesson_filename` is not available, a demo lesson content will be used. - """ - fire.Fire(main) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 4fab92fb3..098388a7c 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -396,9 +396,6 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): logger.warning(f"Exception:{e}, sleeping for {rounded_time} seconds") await asyncio.sleep(rounded_time) continue - except openai.error.APIConnectionError as e: - logger.warning(f"Exception:{e}") - continue except Exception as e: error_str = traceback.format_exc() logger.error(f"Exception:{e}, stack:{error_str}") @@ -414,9 +411,6 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): except openai.error.RateLimitError as e: logger.warning(f"Exception:{e}") continue - except openai.error.APIConnectionError as e: - logger.warning(f"Exception:{e}") - continue except Exception as e: error_str = traceback.format_exc() logger.error(f"Exception:{e}, stack:{error_str}") diff --git a/metagpt/roles/fork_meta_role.py b/metagpt/roles/fork_meta_role.py deleted file mode 100644 index 57d467080..000000000 --- a/metagpt/roles/fork_meta_role.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/7 -@Author : mashenquan -@File : fork_meta_role.py -@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the - ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to - make these symbols configurable and standardized, making the process of building flows more convenient. - For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html` - This file defines a `fork` style meta role capable of generating arbitrary roles at runtime based on a - configuration file. -@Modified By: mashenquan, 2023/8/22. A definition has been provided for the return value of _think: returning false indicates that further reasoning cannot continue. - -""" - -import re - -import aiofiles - -from metagpt.actions.meta_action import MetaAction -from metagpt.const import WORKSPACE_ROOT -from metagpt.logs import logger -from metagpt.roles import Role -from metagpt.roles.uml_meta_role_options import MetaActionOptions, UMLMetaRoleOptions -from metagpt.schema import Message - - -class ForkMetaRole(Role): - """A `fork` style meta role capable of generating arbitrary roles at runtime based on a configuration file""" - def __init__(self, options, cost_manager, role_options, **kwargs): - """Initialize a `fork` style meta role - - :param options: System configuration - :param cost_manager: Cost manager - :param role_options: pattern yaml file data - :param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3` - :param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2` - """ - opts = UMLMetaRoleOptions(**role_options) - global_variables = { - "name": Role.format_value(opts.name, kwargs), - "profile": Role.format_value(opts.profile, kwargs), - "goal": Role.format_value(opts.goal, kwargs), - "constraints": Role.format_value(opts.constraints, kwargs), - "desc": Role.format_value(opts.desc, kwargs), - "role": Role.format_value(opts.role, kwargs) - } - for k, v in kwargs.items(): - if k not in global_variables: - global_variables[k] = v - - super(ForkMetaRole, self).__init__( - options=options, - cost_manager=cost_manager, - name=global_variables["name"], - profile=global_variables["profile"], - goal=global_variables["goal"], - constraints=global_variables["constraints"], - desc=global_variables["desc"], - **kwargs - ) - actions = [] - for m in opts.actions: - for k, v in m.items(): - v = Role.format_value(v, kwargs) - m[k] = v - for k, v in global_variables.items(): - if k not in m: - m[k] = v - - o = MetaActionOptions(**m) - o.set_default_template(opts.templates[o.template_ix]) - - act = MetaAction(options=options, action_options=o, llm=self._llm, **m) - actions.append(act) - self._init_actions(actions) - requirement_types = set() - for v in opts.requirement: - requirement_types.add(MetaAction.get_action_type(v)) - self._watch(requirement_types) - - async def _think(self) -> None: - """Everything will be done part by part.""" - if self._rc.todo is None: - self._set_state(0) - return True - - if self._rc.state + 1 < len(self._states): - self._set_state(self._rc.state + 1) - else: - self._rc.todo = None - return False - - async def _react(self) -> Message: - ret = Message(content="") - while True: - await self._think() - if self._rc.todo is None: - break - logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}") - msg = await self._act() - if ret.content != '': - ret.content += "\n\n\n" - ret.content += msg.content - logger.info(ret.content) - await self.save(ret.content) - return ret - - async def save(self, content): - """Save teaching plan""" - output_filename = self.options.get("output_filename") - if not output_filename: - return - filename = ForkMetaRole.new_file_name(output_filename) - pathname = WORKSPACE_ROOT / "teaching_plan" - pathname.mkdir(exist_ok=True) - pathname = pathname / filename - try: - async with aiofiles.open(str(pathname), mode='w', encoding='utf-8') as writer: - await writer.write(content) - except Exception as e: - logger.error(f'Save failed:{e}') - logger.info(f"Save to:{pathname}") - - @staticmethod - def new_file_name(lesson_title, ext=".md"): - """Create a related file name based on `lesson_title` and `ext`.""" - # Define the special characters that need to be replaced. - illegal_chars = r'[#@$%!*&\\/:*?"<>|\n\t \']' - # Replace the special characters with underscores. - filename = re.sub(illegal_chars, '_', lesson_title) + ext - return re.sub(r'_+', '_', filename) \ No newline at end of file diff --git a/metagpt/roles/uml_meta_role_factory.py b/metagpt/roles/uml_meta_role_factory.py deleted file mode 100644 index 42071b0a6..000000000 --- a/metagpt/roles/uml_meta_role_factory.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/7 -@Author : mashenquan -@File : uml_meta_role_factory.py -@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the - ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to - make these symbols configurable and standardized, making the process of building flows more convenient. - For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html` -""" - -from metagpt.roles.fork_meta_role import ForkMetaRole -from metagpt.roles.uml_meta_role_options import UMLMetaRoleOptions - - -class UMLMetaRoleFactory: - """Factory of UML activity role classes""" - - @classmethod - def create_roles(cls, role_configs, **kwargs): - """Generate the flow of the project based on the configuration in the format of config/pattern/template.yaml. - - :param role_configs: `roles` field of template.yaml - :param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2` - - """ - roles = [] - for m in role_configs: - opt = UMLMetaRoleOptions(**m) - constructor = cls.CONSTRUCTORS.get(opt.role_type) - if constructor is None: - raise NotImplementedError( - f"{opt.role_type} is not implemented" - ) - r = constructor(role_options=m, **kwargs) - roles.append(r) - return roles - - CONSTRUCTORS = { - "fork": ForkMetaRole, - # TODO: add more activity node constructor here.. - } diff --git a/metagpt/roles/uml_meta_role_options.py b/metagpt/roles/uml_meta_role_options.py deleted file mode 100644 index 1d0fb322e..000000000 --- a/metagpt/roles/uml_meta_role_options.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/7 -@Author : mashenquan -@File : uml_meta_role_options.py -@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the - ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to - make these symbols configurable and standardized, making the process of building flows more convenient. - For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html` -""" - -from typing import List, Dict - -from pydantic import BaseModel - - -# `startup` field of config/pattern/template.yaml -class StartupConfig(BaseModel): - requirement: str - role: str - investment: float = 3.0 - n_round: int = 3 - - -# config/pattern/template.yaml -class ProjectConfig(BaseModel): - startup: StartupConfig - roles: List[Dict] - - -# element of `actions` field of config/pattern/template.yaml -class MetaActionOptions(BaseModel): - topic: str - name: str = "" - language: str = "Chinese" - template_ix: int = 0 - statements: List[str] = [] - template: str = "" - rsp_begin_tag: str = "" - rsp_end_tag: str = "" - - def set_default_template(self, v): - if not self.template: - self.template = v - - def format_prompt(self, **kwargs): - statements = "\n".join(self.statements) - opts = kwargs.copy() - opts["statements"] = statements - - from metagpt.roles import Role - prompt = Role.format_value(self.template, opts) - return prompt - - -# element of `roles` field of config/pattern/template.yaml -class UMLMetaRoleOptions(BaseModel): - role_type: str - name: str = "" - profile: str = "" - goal: str = "" - role: str = "" - constraints: str = "" - desc: str = "" - templates: List[str] = [] - output_filename: str = "" - actions: List - requirement: List diff --git a/tests/metagpt/actions/test_meta_action.py b/tests/metagpt/actions/test_meta_action.py deleted file mode 100644 index cbaf3456c..000000000 --- a/tests/metagpt/actions/test_meta_action.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/8 -@Author : mashenquan -@File : test_meta_action.py -""" -from typing import Dict - -from pydantic import BaseModel - -from metagpt.actions.meta_action import MetaAction -from metagpt.roles.uml_meta_role_options import MetaActionOptions - - -def test_meta_action_create(): - class Inputs(BaseModel): - options: Dict - kwargs: Dict - expect_class_name: str - expect_prompt: str - - inputs = [ - { - "options": { - "topic": "TOPIC_A", - "name": "A", - "language": "XX", - "template_ix": 0, - "statements": ["Statement A", "Statement B"], - "template": "{statements}", - "rsp_begin_tag": "", - "rsp_end_tag": "" - }, - "kwargs": {}, - "expect_class_name": "TOPIC_A", - "expect_prompt": "\n".join(["Statement A", "Statement B"]), - } - ] - - for i in inputs: - seed = Inputs(**i) - opt = MetaActionOptions(**seed.options) - act = MetaAction(opt, **seed.kwargs) - assert seed.expect_prompt == act.prompt - t = MetaAction.get_action_type(seed.expect_class_name) - assert t.__name__ == seed.expect_class_name - - -if __name__ == '__main__': - test_meta_action_create() diff --git a/tests/metagpt/roles/test_fork_meta_role.py b/tests/metagpt/roles/test_fork_meta_role.py deleted file mode 100644 index 355197234..000000000 --- a/tests/metagpt/roles/test_fork_meta_role.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/8 -@Author : mashenquan -@File : test_fork_meta_role.py -""" -from typing import Dict - -from pydantic import BaseModel - -from metagpt.config import Config -from metagpt.provider.openai_api import CostManager -from metagpt.roles.fork_meta_role import ForkMetaRole - - -def test_creat_role(): - class Inputs(BaseModel): - role: Dict - action_count: int - - inputs = [ - { - "role": { - "role_type": "fork", - "name": "Lily", - "profile": "{teaching_language} Teacher", - "goal": "writing a {language} teaching plan part by part", - "constraints": "writing in {language}", - "role": "You are a {teaching_language} Teacher, named Lily, your goal is writing a {" - "teaching_language} teaching plan part by part, and the constraint is writing in {language}.", - "desc": "", - "output_filename": "teaching_plan_demo.md", - "requirement": ["TeachingPlanRequirement"], - "templates": [ - "Do not refer to the context of the previous conversation records, start the conversation " - "anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[" - "LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" " - "defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the " - "format requirements for your responses;\n\t\"Constraint\" defines the conditions that your " - "responses must comply with.\n\n{statements}\nConstraint: Writing in {language}.\nAnswer options: " - "Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\n[" - "LESSON_BEGIN]\n{lesson}\n[LESSON_END]", - "Do not refer to the context of the previous conversation records, start the conversation " - "anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[" - "LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" " - "defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the " - "format requirements for your responses;\n\t\"Constraint\" defines the conditions that your " - "responses must comply with.\n\nCapacity and role: {role}\nStatement: Write the \"{topic}\" part " - "of teaching plan, WITHOUT ANY content unrelated to \"{topic}\"!!\n{statements}\nAnswer options: " - "Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" " - "tags.\nAnswer options: Using proper markdown format from second-level header " - "format.\nConstraint: Writing in {language}.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END] " - ], - "actions": [ - { - "name": "", - "topic": "Title", - "language": "Chinese", - "statements": [ - "Statement: Find and return the title of the lesson only with \"# \" prefixed, without " - "anything else."], - "template_ix": 0}, - { - "name": "", - "topic": "Teaching Hours", - "language": "Chinese", - "statements": [], - "template_ix": 1, - "rsp_begin_tag": "[TEACHING_PLAN_BEGIN]", - "rsp_end_tag": "[TEACHING_PLAN_END]"} - ] - }, - "action_count": 2 - } - ] - - for i in inputs: - seed = Inputs(**i) - kwargs = { - "teaching_language": "AA", - "language": "BB" - } - runtime_options = Config().runtime_options - cost_manager = CostManager(options=runtime_options) - role = ForkMetaRole(runtime_options=runtime_options, cost_manager=cost_manager, role_options=seed.role, **kwargs) - assert role.action_count == 2 - assert "{" not in role.profile - assert "{" not in role.goal - assert "{" not in role.constraints - - -if __name__ == '__main__': - test_creat_role() diff --git a/tests/metagpt/roles/test_uml_meta_role_factory.py b/tests/metagpt/roles/test_uml_meta_role_factory.py deleted file mode 100644 index f59a30611..000000000 --- a/tests/metagpt/roles/test_uml_meta_role_factory.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/8 -@Author : mashenquan -@File : test_uml_meta_role_factory.py -""" -from typing import List, Dict - -from pydantic import BaseModel - -from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory - - -def test_create_roles(): - class Inputs(BaseModel): - roles: List - kwargs: Dict - - inputs = [ - { - "roles": [ - { - "role_type": "fork", - "name": "Lily", - "profile": "{teaching_language} Teacher", - "goal": "writing a {language} teaching plan part by part", - "constraints": "writing in {language}", - "role": "You are a {teaching_language} Teacher, named Lily.", - "desc": "", - "output_filename": "teaching_plan_demo.md", - "requirement": ["TeachingPlanRequirement"], - "templates": ["Do 1 {statements}", "Do 2 {statements}"], - "actions": [ - { - "name": "", - "topic": "Title", - "language": "Chinese", - "statements": ["statement 1", "statement 2"]} - ], - "template_ix": 0 - } - ], - "kwargs": { - "teaching_language": "AA", - "language": "BB", - } - } - ] - - for i in inputs: - seed = Inputs(**i) - roles = UMLMetaRoleFactory.create_roles(seed.roles, **seed.kwargs) - assert len(roles) == 1 - assert "{" not in roles[0].profile - assert "{" not in roles[0].goal - assert roles[0].action_count == 1 - - -if __name__ == '__main__': - test_create_roles() diff --git a/tests/metagpt/roles/test_uml_meta_role_options.py b/tests/metagpt/roles/test_uml_meta_role_options.py deleted file mode 100644 index 1eb66c50e..000000000 --- a/tests/metagpt/roles/test_uml_meta_role_options.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/8/8 -@Author : mashenquan -@File : test_uml_meta_role_options.py -""" -from typing import List - -from pydantic import BaseModel - -from metagpt.roles.uml_meta_role_options import MetaActionOptions - - -def test_set_default_template(): - class Inputs(BaseModel): - statements: List - template: str - expect_prompt: str - - inputs = [ - { - "statements": ["Statement: 1", "Statement: 2"], - "template": "{statements}", - "expect_prompt": "Statement: 1\nStatement: 2" - } - ] - - for i in inputs: - seed = Inputs(**i) - opt = MetaActionOptions(topic="", statements=seed.statements) - assert opt.template == "" - opt.set_default_template(seed.template) - assert opt.template == seed.template - kwargs = {} - assert opt.format_prompt(**kwargs) == seed.expect_prompt - - -if __name__ == '__main__': - test_set_default_template()