From 493224439e47cdcf3170d495a6ec35e481404ae8 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Fri, 15 Dec 2023 18:48:43 +0800 Subject: [PATCH 1/7] version preparation --- README.md | 3 ++- config/config.yaml | 3 +++ setup.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3473a12c..b0faf85c7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ # MetaGPT: The Multi-Agent Framework

Software Company Multi-Role Schematic (Gradually Implementing)

- +## News +- Dec 15: v0.5.0 is released! We introduce **incremental development**, facilitating agents to build up larger projects on top of their previous efforts or exisiting human codebase. We also launch a whole collection of important features, including multilingual support (experimental), multiple programming languages support (experimental), incremental development (experimental), CLI support, pip support, enhanced code review, documentation mechanism, and optimized messaging mechanism! ## Install diff --git a/config/config.yaml b/config/config.yaml index 8fd208c59..dc4c4ea5a 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,6 +1,9 @@ # DO NOT MODIFY THIS FILE, create a new key.yaml, define OPENAI_API_KEY. # The configuration of key.yaml has a higher priority and will not enter git +#### Project Path Setting +# WORKSPACE_PATH: "Path for placing output files" + #### if OpenAI ## The official OPENAI_API_BASE is https://api.openai.com/v1 ## If the official OPENAI_API_BASE is not available, we recommend using the [openai-forward](https://github.com/beidongjiedeguang/openai-forward). diff --git a/setup.py b/setup.py index 4dd453b3d..730fffd35 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ with open(path.join(here, "requirements.txt"), encoding="utf-8") as f: setup( name="metagpt", - version="0.4.0", + version="0.5.0", description="The Multi-Role Meta Programming Framework", long_description=long_description, long_description_content_type="text/markdown", From 335a025c030db55f563e5dd21aaa0f2a7e632018 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Fri, 15 Dec 2023 19:38:48 +0800 Subject: [PATCH 2/7] fix cContextVar OPTIONS LookupError --- metagpt/config.py | 2 ++ metagpt/const.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/metagpt/config.py b/metagpt/config.py index 19bd02c87..d7f5c1249 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -45,9 +45,11 @@ class Config(metaclass=Singleton): default_yaml_file = METAGPT_ROOT / "config/config.yaml" def __init__(self, yaml_file=default_yaml_file): + golbal_options = OPTIONS.get() self._init_with_config_files_and_env(yaml_file) logger.debug("Config loading done.") self._update() + golbal_options.update(OPTIONS.get()) def _update(self): # logger.info("Config loading done.") diff --git a/metagpt/const.py b/metagpt/const.py index f6f64a27d..47864d134 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -17,7 +17,7 @@ from loguru import logger import metagpt -OPTIONS = contextvars.ContextVar("OPTIONS") +OPTIONS = contextvars.ContextVar("OPTIONS", default={}) def get_metagpt_package_root(): From 68f3865893140f93f2f38fc5591d0b9cb340c871 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sun, 17 Dec 2023 00:21:43 +0800 Subject: [PATCH 3/7] Add UserRequirement to watch in default if the role is not set to watch --- metagpt/roles/role.py | 14 +++++++++++++- metagpt/schema.py | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index b07541b09..1e7ebf711 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -27,6 +27,7 @@ from pydantic import BaseModel, Field from metagpt.actions import Action, ActionOutput from metagpt.actions.action_node import ActionNode +from metagpt.actions.add_requirement import UserRequirement from metagpt.llm import LLM, HumanProvider from metagpt.logs import logger from metagpt.memory import Memory @@ -126,7 +127,17 @@ class RoleContext(BaseModel): return self.memory.get() -class Role: +class _RoleInjector(type): + def __call__(cls, *args, **kwargs): + instance = super().__call__(*args, **kwargs) + + if not instance._rc.watch: + instance._watch([UserRequirement]) + + return instance + + +class Role(metaclass=_RoleInjector): """Role/Agent""" def __init__(self, name="", profile="", goal="", constraints="", desc="", is_human=False): @@ -141,6 +152,7 @@ class Role: self._rc = RoleContext() self._subscription = {any_to_str(self), name} if name else {any_to_str(self)} + def _reset(self): self._states = [] self._actions = [] diff --git a/metagpt/schema.py b/metagpt/schema.py index 758149efa..5aec378e4 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -121,6 +121,10 @@ class Message(BaseModel): :param send_to: Specifies the target recipient or consumer for message delivery in the environment. :param role: Message meta info tells who sent this message. """ + if not cause_by: + from metagpt.actions import UserRequirement + cause_by = UserRequirement + super().__init__( id=uuid.uuid4().hex, content=content, From 355ee8faa8cf4b2edd0fbeccf2084890da780d6d Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sun, 17 Dec 2023 00:23:21 +0800 Subject: [PATCH 4/7] Set current working directory (cwd) to default project root in PyPI mode --- metagpt/const.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/metagpt/const.py b/metagpt/const.py index 47864d134..10de0ff66 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -23,6 +23,12 @@ OPTIONS = contextvars.ContextVar("OPTIONS", default={}) def get_metagpt_package_root(): """Get the root directory of the installed package.""" package_root = Path(metagpt.__file__).parent.parent + for i in (".git", ".project_root", ".gitignore"): + if (package_root / i).exists(): + break + else: + package_root = Path.cwd() + logger.info(f"Package root set to {str(package_root)}") return package_root From feef54ba3bcbd0d6fe7943f6e9aaf318b9cc396a Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Sun, 17 Dec 2023 13:52:37 +0800 Subject: [PATCH 5/7] patch release v0.5.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 730fffd35..73a05eeae 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ with open(path.join(here, "requirements.txt"), encoding="utf-8") as f: setup( name="metagpt", - version="0.5.0", + version="0.5.1", description="The Multi-Role Meta Programming Framework", long_description=long_description, long_description_content_type="text/markdown", From 097c6e09d2ff7b0f5487f6068ff2185801eb950e Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sun, 17 Dec 2023 14:41:59 +0800 Subject: [PATCH 6/7] add deprecated warnings for the start_project method --- metagpt/team.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/metagpt/team.py b/metagpt/team.py index a5c405f80..5ce07ef13 100644 --- a/metagpt/team.py +++ b/metagpt/team.py @@ -3,10 +3,11 @@ """ @Time : 2023/5/12 00:30 @Author : alexanderwu -@File : software_company.py +@File : team.py @Modified By: mashenquan, 2023/11/27. Add an archiving operation after completing the project, as specified in Section 2.2.3.3 of RFC 135. """ +import warnings from pydantic import BaseModel, Field from metagpt.actions import UserRequirement @@ -47,7 +48,7 @@ class Team(BaseModel): raise NoMoneyException(CONFIG.total_cost, f"Insufficient funds: {CONFIG.max_budget}") def run_project(self, idea, send_to: str = ""): - """Start a project from publishing user requirement.""" + """Run a project from publishing user requirement.""" self.idea = idea # Human requirement. @@ -55,6 +56,16 @@ class Team(BaseModel): Message(role="Human", content=idea, cause_by=UserRequirement, send_to=send_to or MESSAGE_ROUTE_TO_ALL) ) + def start_project(self, idea, send_to: str = ""): + """ + Deprecated: This method will be removed in the future. + Please use the `run_project` method instead. + """ + warnings.warn("The 'start_project' method is deprecated and will be removed in the future. " + "Please use the 'run_project' method instead.", + DeprecationWarning, stacklevel=2) + return self.run_project(idea=idea, send_to=send_to) + def _save(self): logger.info(self.json(ensure_ascii=False)) From 9c405dfa77c81a629f86b82c7721a2389db93472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 18 Dec 2023 16:13:21 +0800 Subject: [PATCH 7/7] fixbug: recursive user requirement dead loop --- metagpt/actions/role_run.py | 16 ++++++++++++++++ metagpt/roles/role.py | 21 +++++++-------------- metagpt/schema.py | 4 ---- 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 metagpt/actions/role_run.py diff --git a/metagpt/actions/role_run.py b/metagpt/actions/role_run.py new file mode 100644 index 000000000..9f0c626b8 --- /dev/null +++ b/metagpt/actions/role_run.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/12/18 +@Author : mashenquan +@File : role_run.py +@Desc : Message type caused by `Role.run()` invocation. +""" +from metagpt.actions import Action + + +class RoleRun(Action): + """Message type caused by `Role.run` invocation""" + + async def run(self, *args, **kwargs): + raise NotImplementedError diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 1e7ebf711..413595c6b 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -27,7 +27,7 @@ from pydantic import BaseModel, Field from metagpt.actions import Action, ActionOutput from metagpt.actions.action_node import ActionNode -from metagpt.actions.add_requirement import UserRequirement +from metagpt.actions.role_run import RoleRun from metagpt.llm import LLM, HumanProvider from metagpt.logs import logger from metagpt.memory import Memory @@ -127,17 +127,7 @@ class RoleContext(BaseModel): return self.memory.get() -class _RoleInjector(type): - def __call__(cls, *args, **kwargs): - instance = super().__call__(*args, **kwargs) - - if not instance._rc.watch: - instance._watch([UserRequirement]) - - return instance - - -class Role(metaclass=_RoleInjector): +class Role: """Role/Agent""" def __init__(self, name="", profile="", goal="", constraints="", desc="", is_human=False): @@ -152,7 +142,6 @@ class Role(metaclass=_RoleInjector): self._rc = RoleContext() self._subscription = {any_to_str(self), name} if name else {any_to_str(self)} - def _reset(self): self._states = [] self._actions = [] @@ -304,7 +293,9 @@ class Role(metaclass=_RoleInjector): old_messages = [] if ignore_memory else self._rc.memory.get() self._rc.memory.add_batch(news) # Filter out messages of interest. - self._rc.news = [n for n in news if n.cause_by in self._rc.watch and n not in old_messages] + watch = self._rc.watch or set() + watch.add(any_to_str(RoleRun)) + self._rc.news = [n for n in news if n.cause_by in watch and n not in old_messages] # Design Rules: # If you need to further categorize Message objects, you can do so using the Message.set_meta function. @@ -401,6 +392,8 @@ class Role(metaclass=_RoleInjector): msg = with_message elif isinstance(with_message, list): msg = Message("\n".join(with_message)) + if not msg.cause_by: + msg.cause_by = RoleRun self.put_message(msg) if not await self._observe(): diff --git a/metagpt/schema.py b/metagpt/schema.py index 5aec378e4..758149efa 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -121,10 +121,6 @@ class Message(BaseModel): :param send_to: Specifies the target recipient or consumer for message delivery in the environment. :param role: Message meta info tells who sent this message. """ - if not cause_by: - from metagpt.actions import UserRequirement - cause_by = UserRequirement - super().__init__( id=uuid.uuid4().hex, content=content,