From 2688fe680adb60e355f7176d439df31b28237db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sat, 4 Nov 2023 14:07:33 +0800 Subject: [PATCH] feat: According to the routing feature plan in Chapter 2.2.3.2 of RFC 113, the routing functionality is to be consolidated into the Environment class. --- metagpt/environment.py | 26 ++++++++++++++++++-------- metagpt/roles/role.py | 9 +++++++-- tests/metagpt/test_role.py | 8 ++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/metagpt/environment.py b/metagpt/environment.py index b93eeb6b2..0fa330a83 100644 --- a/metagpt/environment.py +++ b/metagpt/environment.py @@ -8,9 +8,11 @@ 1. Remove the functionality of `Environment` class as a public message buffer. 2. Standardize the message forwarding behavior of the `Environment` class. 3. Add the `is_idle` property. +@Modified By: mashenquan, 2023-11-4. According to the routing feature plan in Chapter 2.2.3.2 of RFC 113, the routing + functionality is to be consolidated into the `Environment` class. """ import asyncio -from typing import Iterable +from typing import Iterable, Set from pydantic import BaseModel, Field @@ -26,6 +28,7 @@ class Environment(BaseModel): """ roles: dict[str, Role] = Field(default_factory=dict) + consumers: dict[Role, Set] = Field(default_factory=dict) class Config: arbitrary_types_allowed = True @@ -36,6 +39,8 @@ class Environment(BaseModel): """ role.set_env(self) self.roles[role.profile] = role + # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 + self.set_subscribed_tags(role, role.subscribed_tags) def add_roles(self, roles: Iterable[Role]): """增加一批在当前环境的角色 @@ -55,17 +60,14 @@ class Environment(BaseModel): """ logger.info(f"publish_message: {message.save()}") found = False - for r in self.roles.values(): - if message.is_recipient(r.subscribed_tags): - r.put_message(message) + # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 + for obj, subscribed_tags in self.consumers.items(): + if message.is_recipient(subscribed_tags): + obj.put_message(message) found = True if not found: logger.warning(f"Message no recipients: {message.save()}") - # Implemented the functionality related to remote message forwarding as described in RFC 113. Awaiting release. - # if self._parent: - # return self._parent.publish_message(message) - return True async def run(self, k=1): @@ -100,3 +102,11 @@ class Environment(BaseModel): if not r.is_idle: return False return True + + def get_subscribed_tags(self, obj): + """Get the labels for messages to be consumed by the object.""" + return self.consumers.get(obj, {}) + + def set_subscribed_tags(self, obj, tags): + """Set the labels for message to be consumed by the object""" + self.consumers[obj] = tags diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 753c22134..eacaa0034 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -15,6 +15,8 @@ messages into the Role object's private message receive buffer. There are no other message transmit methods. 5. Standardize the parameters for the `run` function: the `test_message` parameter is used for testing purposes only. In the normal workflow, you should use `publish_message` or `put_message` to transmit messages. +@Modified By: mashenquan, 2023-11-4. According to the routing feature plan in Chapter 2.2.3.2 of RFC 113, the routing + functionality is to be consolidated into the `Environment` class. """ from __future__ import annotations @@ -133,7 +135,7 @@ class Role(Named): def _watch(self, actions: Iterable[Type[Action]]): """Listen to the corresponding behaviors""" - tags = [get_class_name(t) for t in actions] + tags = {get_class_name(t) for t in actions} self.subscribe(tags) def subscribe(self, tags: Set[str]): @@ -141,6 +143,8 @@ class Role(Named): self._rc.watch.update(tags) # check RoleContext after adding watch actions self._rc.check(self._role_id) + if self._rc.env: # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 + self._rc.env.set_subscribed_tags(self, self.subscribed_tags) def _set_state(self, state): """Update the current state.""" @@ -149,7 +153,8 @@ class Role(Named): self._rc.todo = self._actions[self._rc.state] def set_env(self, env: "Environment"): - """Set the environment in which the role works. The role can talk to the environment and can also receive messages by observing.""" + """Set the environment in which the role works. The role can talk to the environment and can also receive + messages by observing.""" self._rc.env = env @property diff --git a/tests/metagpt/test_role.py b/tests/metagpt/test_role.py index 829f75bc5..7794c9b57 100644 --- a/tests/metagpt/test_role.py +++ b/tests/metagpt/test_role.py @@ -6,7 +6,11 @@ @File : test_role.py @Modified By: mashenquan, 2023-11-1. In line with Chapter 2.2.1 and 2.2.2 of RFC 116, introduce unit tests for the utilization of the new message distribution feature in message handling. +@Modified By: mashenquan, 2023-11-4. According to the routing feature plan in Chapter 2.2.3.2 of RFC 113, the routing + functionality is to be consolidated into the `Environment` class. """ +import uuid + import pytest from pydantic import BaseModel @@ -64,6 +68,7 @@ async def test_react(): assert role.is_idle env = Environment() env.add_role(role) + assert env.get_subscribed_tags(role) == {seed.subscription} env.publish_message(Message(content="test", tx_to=seed.subscription)) assert not role.is_idle while not env.is_idle: @@ -74,6 +79,9 @@ async def test_react(): while not env.is_idle: await env.run() assert role.is_idle + tag = uuid.uuid4().hex + role.subscribe({tag}) + assert env.get_subscribed_tags(role) == {seed.subscription, tag} if __name__ == "__main__":