Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support async in agents #1178

Merged
merged 8 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion flaml/autogen/agentchat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from .responsive_agent import ResponsiveAgent
from .assistant_agent import AssistantAgent
from .user_proxy_agent import UserProxyAgent
from .groupchat import GroupChatManager
from .groupchat import GroupChat, GroupChatManager

__all__ = [
"Agent",
"ResponsiveAgent",
"AssistantAgent",
"UserProxyAgent",
"GroupChat",
"GroupChatManager",
]
22 changes: 22 additions & 0 deletions flaml/autogen/agentchat/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,23 @@ def name(self):
def send(self, message: Union[Dict, str], recipient: "Agent", request_reply: Optional[bool] = None):
"""(Aabstract method) Send a message to another agent."""

async def a_send(self, message: Union[Dict, str], recipient: "Agent", request_reply: Optional[bool] = None):
"""(Aabstract async method) Send a message to another agent."""

def receive(self, message: Union[Dict, str], sender: "Agent", request_reply: Optional[bool] = None):
"""(Abstract method) Receive a message from another agent."""

async def a_receive(self, message: Union[Dict, str], sender: "Agent", request_reply: Optional[bool] = None):
"""(Abstract async method) Receive a message from another agent."""

def reset(self):
"""(Abstract method) Reset the agent."""

def generate_reply(
self,
messages: Optional[List[Dict]] = None,
sender: Optional["Agent"] = None,
**kwargs,
) -> Union[str, Dict, None]:
"""(Abstract method) Generate a reply based on the received messages.

Expand All @@ -46,3 +53,18 @@ def generate_reply(
Returns:
str or dict or None: the generated reply. If None, no reply is generated.
"""

async def a_generate_reply(
self,
messages: Optional[List[Dict]] = None,
sender: Optional["Agent"] = None,
**kwargs,
) -> Union[str, Dict, None]:
"""(Abstract async method) Generate a reply based on the received messages.

Args:
messages (list[dict]): a list of messages received.
sender: sender of an Agent instance.
Returns:
str or dict or None: the generated reply. If None, no reply is generated.
"""
2 changes: 1 addition & 1 deletion flaml/autogen/agentchat/contrib/math_user_proxy_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def __init__(
default_auto_reply=default_auto_reply,
**kwargs,
)
self.register_auto_reply(Agent, self._generate_math_reply, 1)
self.register_auto_reply(Agent, MathUserProxyAgent._generate_math_reply, 1)
# fixed var
self._max_invalid_q_per_step = max_invalid_q_per_step

Expand Down
94 changes: 55 additions & 39 deletions flaml/autogen/agentchat/groupchat.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
from dataclasses import dataclass
import sys
from typing import Dict, List, Optional, Union
from .agent import Agent
from .responsive_agent import ResponsiveAgent


class GroupChatManager(ResponsiveAgent):
"""(WIP) A chat manager agent that can manage a group chat of multiple agents."""
@dataclass
class GroupChat:
"""A group chat class that contains a list of agents and the maximum number of rounds."""

agents: List[Agent]
max_round: int
messages: List[Dict]
max_round: int = 10

def _participant_roles(self):
return "\n".join([f"{agent.name}: {agent.system_message}" for agent in self.agents])
@property
def agent_names(self) -> List[str]:
"""Return the names of the agents in the group chat."""
return [agent.name for agent in self.agents]

def reset(self):
"""Reset the group chat."""
self.messages.clear()

def agent_by_name(self, name: str) -> Agent:
"""Find the next speaker based on the message."""
return self.agents[self.agent_names.index(name)]

def _select_speaker_msg(self):
def next_agent(self, agent: Agent) -> Agent:
"""Return the next agent in the list."""
return self.agents[(self.agent_names.index(agent.name) + 1) % len(self.agents)]

def select_speaker_msg(self):
"""Return the message for selecting the next speaker."""
return f"""You are in a role play game. The following roles are available:
{self._participant_roles()}. Read the following conversation.
Then select the next role from {self._agent_names} to play. Only return the role."""
Then select the next role from {self.agent_names} to play. Only return the role."""

def select_speaker(self, last_speaker: Agent, selctor: ResponsiveAgent):
"""Select the next speaker."""
selctor.update_system_message(self.select_speaker_msg())
final, name = selctor.generate_oai_reply(self.messages)
if not final:
# i = self._random.randint(0, len(self._agent_names) - 1) # randomly pick an id
return self.next_agent(last_speaker)
try:
return self.agent_by_name(name)
except ValueError:
return self.next_agent(last_speaker)

def _participant_roles(self):
return "\n".join([f"{agent.name}: {agent.system_message}" for agent in self.agents])


class GroupChatManager(ResponsiveAgent):
"""(WIP) A chat manager agent that can manage a group chat of multiple agents."""

def __init__(
self,
max_round: Optional[int] = 10,
groupchat: GroupChat,
name: Optional[str] = "chat_manager",
# unlimited consecutive auto reply by default
max_consecutive_auto_reply: Optional[int] = sys.maxsize,
Expand All @@ -33,56 +70,35 @@ def __init__(
name=name,
max_consecutive_auto_reply=max_consecutive_auto_reply,
human_input_mode=human_input_mode,
system_message=system_message,
**kwargs,
)
self.register_auto_reply(Agent, self._generate_reply_for_participant)
self.max_round = max_round
self._agent_names = []
self._messages = []
self.register_auto_reply(Agent, GroupChatManager.run_chat, context=groupchat, reset_context=GroupChat.reset)
# self._random = random.Random(seed)

def _generate_reply_for_participant(
def run_chat(
self,
messages: Optional[List[Dict]] = None,
sender: Optional[Agent] = None,
context: Optional[GroupChat] = None,
) -> Union[str, Dict, None]:
self._agent_names = [agent.name for agent in self.agents]
"""Run a group chat."""
if messages is None:
messages = self._oai_messages[sender]
message = messages[-1]
speaker = sender
for i in range(self.max_round):
for i in range(context.max_round):
# set the name to speaker's name if the role is not function
if message["role"] != "function":
message["name"] = speaker.name
self._messages.append(message)
context.messages.append(message)
# broadcast the message to all agents except the speaker
for agent in self.agents:
for agent in context.agents:
if agent != speaker:
self.send(message, agent, request_reply=False)
if i != self.max_round - 1:
if i != context.max_round - 1:
# speaker selection msg from an agent
speaker = self._select_speaker(speaker)
speaker = context.select_speaker(speaker, self)
speaker.send(speaker.generate_reply(sender=self), self, request_reply=False)
message = self.last_message(speaker)
return True, None

def _select_speaker(self, last_speaker: Agent):
"""Select the next speaker."""
self.update_system_message(self._select_speaker_msg())
final, name = self._generate_oai_reply(self._messages)
if not final:
# i = self._random.randint(0, len(self._agent_names) - 1) # randomly pick an id
return self.agents[(self._agent_names.index(last_speaker.name) + 1) % len(self._agent_names)]
try:
return self.agent_by_name(name)
except ValueError:
return self.agents[(self._agent_names.index(last_speaker.name) + 1) % len(self._agent_names)]

def agent_by_name(self, name: str) -> Agent:
"""Find the next speaker based on the message."""
return self.agents[self._agent_names.index(name)]

def reset(self):
super().reset()
self._messages.clear()
Loading
Loading