diff --git a/metagpt/environment/mgx/mgx_env.py b/metagpt/environment/mgx/mgx_env.py index a8fc0df9f4..856f697d42 100644 --- a/metagpt/environment/mgx/mgx_env.py +++ b/metagpt/environment/mgx/mgx_env.py @@ -27,10 +27,17 @@ def publish_message(self, message: Message, user_defined_recipient: str = "", pu tl = self.get_role(TEAMLEADER_NAME) # TeamLeader's name is Mike + # If there is no TeamLeader in the environment, fall back to the regular publishing flow + if tl is None: + self._publish_message(message) + self.history.add(message) + return True + if user_defined_recipient: # human user's direct chat message to a certain role for role_name in message.send_to: - if self.get_role(role_name).is_idle: + role_obj = self.get_role(role_name) + if role_obj and role_obj.is_idle: # User starts a new direct chat with a certain role, expecting a direct chat response from the role; Other roles including TL should not be involved. # If the role is not idle, it means the user helps the role with its current work, in this case, we handle the role's response message as usual. self.direct_chat_roles.add(role_name) diff --git a/tests/metagpt/environment/test_mgx_env.py b/tests/metagpt/environment/test_mgx_env.py new file mode 100644 index 0000000000..8a029c5c68 --- /dev/null +++ b/tests/metagpt/environment/test_mgx_env.py @@ -0,0 +1,61 @@ +import pytest + +from metagpt.environment.mgx.mgx_env import MGXEnv +from metagpt.schema import Message + + +def test_publish_message_without_team_leader_falls_back_and_records_history(): + env = MGXEnv() + before_count = env.history.count() + + msg = Message(content="hi", role="user") + + # Should not raise and should return True + assert env.publish_message(msg) is True + + # History should increase (once in base env, and once in MGXEnv) + assert env.history.count() >= before_count + 1 + + # The transformed message (from _publish_message) should be present in history + # It includes the sender/receiver info moved into content + assert any( + "[Message] from" in m.content and "to" in m.content for m in env.history.storage + ) + + +def test_user_defined_recipient_with_missing_role_is_safe_and_no_direct_chat_added(): + env = MGXEnv() + before_count = env.history.count() + + # Direct chat to a non-existent role should not crash and should not add to direct_chat_roles + msg = Message(content="ping", role="user") + msg.send_to = {"NonExistentRole"} + + assert env.publish_message(msg, user_defined_recipient="NonExistentRole") is True + + # No direct chat role should be recorded for a missing role + assert "NonExistentRole" not in env.direct_chat_roles + + # Still publishes and records in history + assert env.history.count() >= before_count + 1 + + +def test_user_defined_recipient_with_existing_role_does_not_error(): + env = MGXEnv() + + # Define a minimal role by name and add to env + from metagpt.roles import Role + + class DummyRole(Role): + name: str = "Alice" + profile: str = "DummyRole" + + env.add_roles([DummyRole()]) + + msg = Message(content="hello", role="user") + msg.send_to = {"Alice"} + + # Should not raise and should return True + assert env.publish_message(msg, user_defined_recipient="Alice") is True + +