From 010022c3672a64e64d95fb61e157fbfa94965d49 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Fri, 8 Mar 2024 18:35:48 +0800 Subject: [PATCH 01/14] add groupchat with @ --- examples/groupchat/configs/agent_configs.json | 29 ++++++++++ examples/groupchat/configs/model_configs.json | 19 +++++++ examples/groupchat/groupchat_utils.py | 36 ++++++++++++ examples/groupchat/main.py | 57 +++++++++++++++++++ examples/groupchat/prompt.py | 10 ++++ src/agentscope/utils/common.py | 3 + 6 files changed, 154 insertions(+) create mode 100644 examples/groupchat/configs/agent_configs.json create mode 100644 examples/groupchat/configs/model_configs.json create mode 100644 examples/groupchat/groupchat_utils.py create mode 100644 examples/groupchat/main.py create mode 100644 examples/groupchat/prompt.py diff --git a/examples/groupchat/configs/agent_configs.json b/examples/groupchat/configs/agent_configs.json new file mode 100644 index 000000000..42a35aadd --- /dev/null +++ b/examples/groupchat/configs/agent_configs.json @@ -0,0 +1,29 @@ +[ + { + "class": "DialogAgent", + "args": { + "name": "zhenhuan", + "sys_prompt":"你是甄嬛,你正在想办法除掉皇后,你说话风格为文言文", + "model_config_name": "gpt-4", + "use_memory": true + } + }, + { + "class": "DialogAgent", + "args": { + "name": "ikun", + "sys_prompt":"你是蔡徐坤的粉丝,说话很简短,喜欢用颜文字表达心情,你最近迷恋看《甄嬛传》", + "model_config_name": "gpt-4", + "use_memory": true + } + }, + { + "class": "DialogAgent", + "args": { + "name": "datou", + "sys_prompt":"你大头,是一个体育生,热爱运动,你不喜欢追星,你喜欢安利别人健身", + "model_config_name": "gpt-4", + "use_memory": true + } + } +] \ No newline at end of file diff --git a/examples/groupchat/configs/model_configs.json b/examples/groupchat/configs/model_configs.json new file mode 100644 index 000000000..1d1b7193c --- /dev/null +++ b/examples/groupchat/configs/model_configs.json @@ -0,0 +1,19 @@ +[ + { + "model_type": "openai", + "config_name": "gpt-4", + "model_name": "gpt-4", + "api_key": "xxx", + "organization": "xxx", + "generate_args": { + "temperature": 0.5 + } + }, + { + "model_type": "post_api_chat", + "config_name": "my_post_api", + "api_url": "https://xxx", + "headers": {}, + "json_args": {} + } +] \ No newline at end of file diff --git a/examples/groupchat/groupchat_utils.py b/examples/groupchat/groupchat_utils.py new file mode 100644 index 000000000..9142883bd --- /dev/null +++ b/examples/groupchat/groupchat_utils.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" Group chat utils.""" +import re + + +def select_next_one(agents: list, rnd: int) -> list: + """ + Select next agent. + """ + return agents[rnd % len(agents)] + + +def filter_names(string: str, agents: list) -> list: + """ + This function filters the input string for occurrences of the given names + prefixed with '@' and returns a list of the found names. + """ + if len(agents) == 0: + return [] + + # Create a pattern that matches @ followed by any of the candidate names + pattern = ( + r"@(" + "|".join(re.escape(agent.name) for agent in agents) + r")\b" + ) + + # Find all occurrences of the pattern in the string + matches = re.findall(pattern, string) + + # Create a dictionary mapping agent names to agent objects for quick lookup + agent_dict = {agent.name: agent for agent in agents} + + # Return the list of matched agent objects preserving the order + ordered_agents = [ + agent_dict[name] for name in matches if name in agent_dict + ] + return ordered_agents diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py new file mode 100644 index 000000000..855720033 --- /dev/null +++ b/examples/groupchat/main.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" A group chat where user can talk any time implemented by agentscope. """ +from prompt import Prompts +from groupchat_utils import ( + select_next_one, + filter_names, +) + +import agentscope +from agentscope.agents import UserAgent +from agentscope.message import Msg +from agentscope.msghub import msghub +from agentscope.utils.common import timer + +USER_TIME_TO_SPEAK = 10 + + +def main() -> None: + """group chat""" + npc_agents = agentscope.init( + model_configs="./configs/model_configs.json", + agent_configs="./configs/agent_configs.json", + ) + + user = UserAgent() + + agents = npc_agents + [user] + + hint = Msg(name="Host", content=Prompts.opening) + + rnd = 0 + speak_list = [] + with msghub(agents, announcement=hint): + while True: + try: + with timer(USER_TIME_TO_SPEAK): + x = user() + except TimeoutError: + x = {"content": ""} + print("\n") + + speak_list += filter_names(x.get("content", ""), npc_agents) + + if len(speak_list) > 0: + next_agent = speak_list.pop(0) + x = next_agent() + else: + next_agent = select_next_one(npc_agents, rnd) + x = next_agent() + + speak_list += filter_names(x.content, npc_agents) + + rnd += 1 + + +if __name__ == "__main__": + main() diff --git a/examples/groupchat/prompt.py b/examples/groupchat/prompt.py new file mode 100644 index 000000000..4d391493b --- /dev/null +++ b/examples/groupchat/prompt.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +"""Used to record prompts, will be replaced by configuration""" + + +class Prompts: + """Prompts for group chat""" + + opening = """ + 这是一个聊天室,你可以随意发言。 + """ diff --git a/src/agentscope/utils/common.py b/src/agentscope/utils/common.py index ea8212697..0db805d91 100644 --- a/src/agentscope/utils/common.py +++ b/src/agentscope/utils/common.py @@ -26,6 +26,9 @@ def timer(seconds: Optional[Union[int, float]] = None) -> Generator: Note: This function only works in Unix, since `signal.setitimer` is only available in Unix. + + This function may cause error when not used in main thread, + need to fix. """ if seconds is None or sys.platform == "win32": yield From 4372c1d0d7eead1755025e8d8d545e0d9bb0e8c9 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Mon, 11 Mar 2024 10:45:03 +0800 Subject: [PATCH 02/14] update --- examples/groupchat/README.md | 1 + examples/groupchat/configs/agent_configs.json | 12 ++++++------ examples/groupchat/groupchat_utils.py | 5 +++-- examples/groupchat/main.py | 8 +++++--- examples/groupchat/prompt.py | 10 ---------- src/agentscope/utils/common.py | 2 +- 6 files changed, 16 insertions(+), 22 deletions(-) create mode 100644 examples/groupchat/README.md delete mode 100644 examples/groupchat/prompt.py diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md new file mode 100644 index 000000000..2fd9f9570 --- /dev/null +++ b/examples/groupchat/README.md @@ -0,0 +1 @@ +TBD \ No newline at end of file diff --git a/examples/groupchat/configs/agent_configs.json b/examples/groupchat/configs/agent_configs.json index 42a35aadd..35b5d0731 100644 --- a/examples/groupchat/configs/agent_configs.json +++ b/examples/groupchat/configs/agent_configs.json @@ -2,8 +2,8 @@ { "class": "DialogAgent", "args": { - "name": "zhenhuan", - "sys_prompt":"你是甄嬛,你正在想办法除掉皇后,你说话风格为文言文", + "name": "Lingfeng", + "sys_prompt":"You are Lingfeng, a noble in the imperial court, known for your wisdom and strategic acumen. You often engage in complex political intrigues and have recently suspected the Queen’s adviser of treachery. Your speaking style is reminiscent of classical literature.", "model_config_name": "gpt-4", "use_memory": true } @@ -11,8 +11,8 @@ { "class": "DialogAgent", "args": { - "name": "ikun", - "sys_prompt":"你是蔡徐坤的粉丝,说话很简短,喜欢用颜文字表达心情,你最近迷恋看《甄嬛传》", + "name": "Boyu", + "sys_prompt":"You are Boyu, a friend of Lingfeng and an enthusiast of court dramas. Your speech is modern but with a flair for the dramatic, matching your love for emotive storytelling. You've been closely following Lingfeng’s political maneuvers in the imperial court through secret correspondence.", "model_config_name": "gpt-4", "use_memory": true } @@ -20,8 +20,8 @@ { "class": "DialogAgent", "args": { - "name": "datou", - "sys_prompt":"你大头,是一个体育生,热爱运动,你不喜欢追星,你喜欢安利别人健身", + "name": "Haotian", + "sys_prompt":"You are Haotian, Lingfeng’s cousin who prefers the open fields to the confines of court life. As a celebrated athlete, your influence has protected Lingfeng in times of political strife. You promote physical training as a way to prepare for life's battles, often using sports metaphors in conversation.", "model_config_name": "gpt-4", "use_memory": true } diff --git a/examples/groupchat/groupchat_utils.py b/examples/groupchat/groupchat_utils.py index 9142883bd..91760bd35 100644 --- a/examples/groupchat/groupchat_utils.py +++ b/examples/groupchat/groupchat_utils.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- """ Group chat utils.""" import re +from typing import Sequence -def select_next_one(agents: list, rnd: int) -> list: +def select_next_one(agents: Sequence, rnd: int) -> Sequence: """ Select next agent. """ return agents[rnd % len(agents)] -def filter_names(string: str, agents: list) -> list: +def filter_names(string: str, agents: Sequence) -> Sequence: """ This function filters the input string for occurrences of the given names prefixed with '@' and returns a list of the found names. diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index 855720033..2d3ef34b8 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """ A group chat where user can talk any time implemented by agentscope. """ -from prompt import Prompts from groupchat_utils import ( select_next_one, filter_names, @@ -24,9 +23,12 @@ def main() -> None: user = UserAgent() - agents = npc_agents + [user] + agents = list(npc_agents) + [user] - hint = Msg(name="Host", content=Prompts.opening) + hint = Msg( + name="Host", + content="This is a chat room and you can speak freely.", + ) rnd = 0 speak_list = [] diff --git a/examples/groupchat/prompt.py b/examples/groupchat/prompt.py deleted file mode 100644 index 4d391493b..000000000 --- a/examples/groupchat/prompt.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -"""Used to record prompts, will be replaced by configuration""" - - -class Prompts: - """Prompts for group chat""" - - opening = """ - 这是一个聊天室,你可以随意发言。 - """ diff --git a/src/agentscope/utils/common.py b/src/agentscope/utils/common.py index 0db805d91..ee7e3a2cd 100644 --- a/src/agentscope/utils/common.py +++ b/src/agentscope/utils/common.py @@ -27,7 +27,7 @@ def timer(seconds: Optional[Union[int, float]] = None) -> Generator: This function only works in Unix, since `signal.setitimer` is only available in Unix. - This function may cause error when not used in main thread, + TODO: This function may cause error when not used in main thread, need to fix. """ if seconds is None or sys.platform == "win32": From 3c825322eca729613951b1e08bc43a1ce508cb6f Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Mon, 11 Mar 2024 11:28:59 +0800 Subject: [PATCH 03/14] update --- examples/groupchat/README.md | 15 ++++++++++++++- examples/groupchat/main.py | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index 2fd9f9570..ffae36890 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -1 +1,14 @@ -TBD \ No newline at end of file +# Multi-Agent Group Conversation in AgentScope + +This example demonstrates a multi-agent group conversation using AgentScope. The script `main.py` orchestrates a group chat where a user agent and several assistant agents interact dynamically in a group conversation. The chat continues until the user types "exit", signaling the end of the session. + +## Features + +* Implements a real-time group conversation with multiple agents. +* User-driven conversation flows with easy-to-modify agent roles via `sys_prompt`. +* Round-based agent conversation selection with timeout for user agent. + +```bash +# Note: Set your api_key in configs/model_configs.json first +python main.py +``` diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index 2d3ef34b8..c23a623f5 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -37,6 +37,8 @@ def main() -> None: try: with timer(USER_TIME_TO_SPEAK): x = user() + if x.content == "exit": + break except TimeoutError: x = {"content": ""} print("\n") From 43b88b51bbb62fb55009af7edd077c1d0c168223 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 14:58:17 +0800 Subject: [PATCH 04/14] update doc --- examples/groupchat/README.md | 73 ++++++++++++++++++++-- examples/groupchat/main.py | 3 +- src/agentscope/agents/dict_dialog_agent.py | 2 +- ty.py | 0 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 ty.py diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index ffae36890..ae5090bf2 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -1,14 +1,75 @@ # Multi-Agent Group Conversation in AgentScope -This example demonstrates a multi-agent group conversation using AgentScope. The script `main.py` orchestrates a group chat where a user agent and several assistant agents interact dynamically in a group conversation. The chat continues until the user types "exit", signaling the end of the session. +This example demonstrates a multi-agent group conversation facilitated by AgentScope. The script `main.py` sets up a virtual chat room where a user agent interacts with several NPC (non-player character) agents. The chat utilizes a special **"@"** mention functionality, which allows participants to address specific agents and have a more directed conversation. -## Features +## Key Features -* Implements a real-time group conversation with multiple agents. -* User-driven conversation flows with easy-to-modify agent roles via `sys_prompt`. -* Round-based agent conversation selection with timeout for user agent. +- **Real-time Group Conversation**: Engage in a chat with multiple agents responding in real time. +- **@ Mention Functionality**: Use the "@" symbol followed by an agent's name to specifically address that agent within the conversation. +- **Dynamic Flow**: User-driven conversation with agents responding based on the context and mentions. +- **Configurable Agent Roles**: Easily modify agent roles and behaviors by editing the `sys_prompt` in the configuration files. +- **User Timeout**: If the user does not respond within a specified time, the conversation continues with the next agent. + +## How to Use + +To start the group conversation, follow these steps: + +1. Make sure to set your `api_key` in the `configs/model_configs.json` file. +2. Run the script using the following command: ```bash -# Note: Set your api_key in configs/model_configs.json first python main.py ``` + +1. To address a specific agent in the chat, type "@" followed by the agent's name in your message. +2. To exit the chat, simply type "exit" when it's your turn to speak. + +## Background and Conversation Flow + +The conversation takes place in a simulated chat room environment with roles defined for each participant. The user acts as a regular chat member with the ability to speak freely and address any agent. NPC agents are pre-configured with specific roles that determine their responses and behavior in the chat. The topic of the conversation is open-ended and can evolve organically based on the user's input and agents' programmed personas. + +### Example Interaction + +``` +User: Hi, everyone! I'm excited to join this chat. +AgentA: Welcome! We're glad to have you here. +User: @AgentB, what do you think about the new technology trends? +AgentB: It's an exciting time for tech! There are so many innovations on the horizon. +``` + +## Customization Options + +The group conversation script provides several options for customization, allowing you to tailor the chat experience to your preferences. + +You can customize the conversation by editing the agent configurations and model parameters. The `agent_configs.json` file allows you to set specific behaviors for each NPC agent, while `model_configs.json` contains the parameters for the conversation model. + +### Changing User Input Time Limit + +The `USER_TIME_TO_SPEAK` variable sets the time limit (in seconds) for the user to input their message during each round. By default, this is set to 10 seconds. You can adjust this time limit by modifying the value of `USER_TIME_TO_SPEAK` in the `main.py` script. + +For example, to change the time limit to 20 seconds, update the line in `main.py` as follows: + +``` +1USER_TIME_TO_SPEAK = 20 # User has 20 seconds to type their message +``` + +### Setting a Default Topic for the Chat Room + +The `DEFAULT_TOPIC` variable defines the initial message or topic of the chat room. It sets the stage for the conversation and is announced at the beginning of the chat session. You can change this message to prompt a specific discussion topic or to provide instructions to the agents. + +To customize this message, modify the `DEFAULT_TOPIC` variable in the `main.py` script. For instance, if you want to set the default topic to discuss "The Future of Artificial Intelligence," you would change the code as follows: + +```python +DEFAULT_TOPIC = "Let's discuss the future of Artificial Intelligence." +``` + +Remember to update the hint message sent to the `msghub` accordingly: + +```python +hint = Msg( + name="Host", + content=DEFAULT_TOPIC, +) +``` + +With these customizations, the chat room can be tailored to fit specific themes or time constraints, enhancing the user's control over the chat experience. diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index c23a623f5..e62d710de 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -12,6 +12,7 @@ from agentscope.utils.common import timer USER_TIME_TO_SPEAK = 10 +DEFAULT_TOPIC = "This is a chat room and you can speak freely." def main() -> None: @@ -27,7 +28,7 @@ def main() -> None: hint = Msg( name="Host", - content="This is a chat room and you can speak freely.", + content=DEFAULT_TOPIC, ) rnd = 0 diff --git a/src/agentscope/agents/dict_dialog_agent.py b/src/agentscope/agents/dict_dialog_agent.py index be376105a..eb25b2fd8 100644 --- a/src/agentscope/agents/dict_dialog_agent.py +++ b/src/agentscope/agents/dict_dialog_agent.py @@ -145,7 +145,7 @@ def reply(self, x: dict = None) -> dict: ).raw # logging raw messages in debug mode - logger.debug(json.dumps(response, indent=4)) + logger.debug(json.dumps(response, ensure_ascii=False, indent=4)) # In this agent, if the response is a dict, we treat "speak" as a # special key, which represents the text to be spoken diff --git a/ty.py b/ty.py new file mode 100644 index 000000000..e69de29bb From d6cef4b70e3639d381408c4f472d09e88eaf2f66 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 17:53:19 +0800 Subject: [PATCH 05/14] remove index --- examples/groupchat/README.md | 2 +- ty.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 ty.py diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index ae5090bf2..754b5b905 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -50,7 +50,7 @@ The `USER_TIME_TO_SPEAK` variable sets the time limit (in seconds) for the user For example, to change the time limit to 20 seconds, update the line in `main.py` as follows: ``` -1USER_TIME_TO_SPEAK = 20 # User has 20 seconds to type their message +USER_TIME_TO_SPEAK = 20 # User has 20 seconds to type their message ``` ### Setting a Default Topic for the Chat Room diff --git a/ty.py b/ty.py deleted file mode 100644 index e69de29bb..000000000 From 7fa12516d509617a43a00ad6491d574709c5dded Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:25:00 +0800 Subject: [PATCH 06/14] fix timeout error --- examples/groupchat/main.py | 14 ++++++++------ setup.py | 1 + src/agentscope/agents/user_agent.py | 6 +++++- src/agentscope/utils/common.py | 11 +++++++---- src/agentscope/web/studio/utils.py | 24 +++++++++++++++++++++--- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index e62d710de..497b42258 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """ A group chat where user can talk any time implemented by agentscope. """ +from loguru import logger from groupchat_utils import ( select_next_one, filter_names, @@ -9,7 +10,6 @@ from agentscope.agents import UserAgent from agentscope.message import Msg from agentscope.msghub import msghub -from agentscope.utils.common import timer USER_TIME_TO_SPEAK = 10 DEFAULT_TOPIC = "This is a chat room and you can speak freely." @@ -36,13 +36,15 @@ def main() -> None: with msghub(agents, announcement=hint): while True: try: - with timer(USER_TIME_TO_SPEAK): - x = user() - if x.content == "exit": - break + x = user(timeout=USER_TIME_TO_SPEAK) + if x.content == "exit": + break except TimeoutError: x = {"content": ""} - print("\n") + logger.info( + f"User has not typed text for " + f"{USER_TIME_TO_SPEAK} seconds, skip.", + ) speak_list += filter_names(x.get("content", ""), npc_agents) diff --git a/setup.py b/setup.py index 2a9e6a99f..e3c7c28f7 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ "tiktoken", "Pillow", "requests", + "inputimeout", "openai>=1.3.0", "numpy", "Flask==3.0.0", diff --git a/src/agentscope/agents/user_agent.py b/src/agentscope/agents/user_agent.py index 334c42ed9..54b8a59c5 100644 --- a/src/agentscope/agents/user_agent.py +++ b/src/agentscope/agents/user_agent.py @@ -33,6 +33,7 @@ def reply( self, x: dict = None, required_keys: Optional[Union[list[str], str]] = None, + timeout: Optional[int] = None, ) -> dict: """ Processes the input provided by the user and stores it in memory, @@ -51,6 +52,9 @@ def reply( (`Optional[Union[list[str], str]]`, defaults to `None`): Strings that requires user to input, which will be used as the key of the returned dict. Defaults to None. + timeout (`Optional[int]`, defaults to `None`): + Raise `TimeoutError` if user exceed input time, set to None + for no limit. Returns: `dict`: A dictionary representing the message object that contains @@ -63,7 +67,7 @@ def reply( # TODO: To avoid order confusion, because `input` print much quicker # than logger.chat time.sleep(0.5) - content = user_input() + content = user_input(timeout=timeout) kwargs = {} if required_keys is not None: diff --git a/src/agentscope/utils/common.py b/src/agentscope/utils/common.py index ee7e3a2cd..52663a72d 100644 --- a/src/agentscope/utils/common.py +++ b/src/agentscope/utils/common.py @@ -7,6 +7,7 @@ import signal import sys import tempfile +import threading from typing import Any, Generator, Optional, Union from loguru import logger import requests @@ -24,13 +25,15 @@ def timer(seconds: Optional[Union[int, float]] = None) -> Generator: https://github.com/openai/human-eval/blob/master/human_eval/execution.py Note: - This function only works in Unix, + This function only works in Unix and MainThread, since `signal.setitimer` is only available in Unix. - TODO: This function may cause error when not used in main thread, - need to fix. """ - if seconds is None or sys.platform == "win32": + if ( + seconds is None + or sys.platform == "win32" + or threading.currentThread().name != "MainThread" + ): yield return diff --git a/src/agentscope/web/studio/utils.py b/src/agentscope/web/studio/utils.py index 503e35516..dd169fe8a 100644 --- a/src/agentscope/web/studio/utils.py +++ b/src/agentscope/web/studio/utils.py @@ -5,6 +5,7 @@ from typing import Optional import hashlib from multiprocessing import Queue +from queue import Empty from collections import defaultdict from PIL import Image @@ -91,12 +92,20 @@ def send_player_input(msg: str, uid: Optional[str] = None) -> None: def get_player_input( + timeout: Optional[int] = None, uid: Optional[str] = None, ) -> str: """Gets player input from the web UI or command line.""" global glb_uid_dict glb_queue_user_input = glb_uid_dict[uid]["glb_queue_user_input"] - content = glb_queue_user_input.get(block=True)[1] + + if timeout: + try: + content = glb_queue_user_input.get(block=True, timeout=timeout)[1] + except Empty as error: + raise TimeoutError("timed out") from error + else: + content = glb_queue_user_input.get(block=True)[1] if content == "**Reset**": glb_uid_dict[uid] = init_uid_queues() raise ResetException @@ -180,12 +189,21 @@ def audio2text(audio_path: str) -> str: return " ".join([s["text"] for s in result["output"]["sentence"]]) -def user_input() -> str: +def user_input(timeout: Optional[int] = None) -> str: """get user input""" if hasattr(thread_local_data, "uid"): content = get_player_input( + timeout=timeout, uid=thread_local_data.uid, ) else: - content = input("User input: ") + if timeout: + from inputimeout import inputimeout, TimeoutOccurred + + try: + content = inputimeout("User input: ", timeout=timeout) + except TimeoutOccurred as error: + raise TimeoutError("timed out") from error + else: + content = input("User input: ") return content From 3f1d78cddc520f89171fd1a91b214d5335d2de78 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:25:39 +0800 Subject: [PATCH 07/14] fix timeout error --- src/agentscope/web/studio/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/agentscope/web/studio/utils.py b/src/agentscope/web/studio/utils.py index dd169fe8a..8613b4fdf 100644 --- a/src/agentscope/web/studio/utils.py +++ b/src/agentscope/web/studio/utils.py @@ -102,8 +102,8 @@ def get_player_input( if timeout: try: content = glb_queue_user_input.get(block=True, timeout=timeout)[1] - except Empty as error: - raise TimeoutError("timed out") from error + except Empty as exc: + raise TimeoutError("timed out") from exc else: content = glb_queue_user_input.get(block=True)[1] if content == "**Reset**": @@ -202,8 +202,8 @@ def user_input(timeout: Optional[int] = None) -> str: try: content = inputimeout("User input: ", timeout=timeout) - except TimeoutOccurred as error: - raise TimeoutError("timed out") from error + except TimeoutOccurred as exc: + raise TimeoutError("timed out") from exc else: content = input("User input: ") return content From 836adf265666317ffe273b6cbb1893560f58b5c6 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:35:01 +0800 Subject: [PATCH 08/14] update prompt --- examples/groupchat/groupchat_utils.py | 2 +- examples/groupchat/main.py | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/groupchat/groupchat_utils.py b/examples/groupchat/groupchat_utils.py index 91760bd35..24d422c57 100644 --- a/examples/groupchat/groupchat_utils.py +++ b/examples/groupchat/groupchat_utils.py @@ -11,7 +11,7 @@ def select_next_one(agents: Sequence, rnd: int) -> Sequence: return agents[rnd % len(agents)] -def filter_names(string: str, agents: Sequence) -> Sequence: +def filter_agents(string: str, agents: Sequence) -> Sequence: """ This function filters the input string for occurrences of the given names prefixed with '@' and returns a list of the found names. diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index 497b42258..f6e2f5784 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -3,7 +3,7 @@ from loguru import logger from groupchat_utils import ( select_next_one, - filter_names, + filter_agents, ) import agentscope @@ -12,7 +12,15 @@ from agentscope.msghub import msghub USER_TIME_TO_SPEAK = 10 -DEFAULT_TOPIC = "This is a chat room and you can speak freely." +DEFAULT_TOPIC = """ +This is a chat room and you can speak freely and briefly. + +You can designate a member to reply to your message, you can use the @ symbol. +This means including the @ symbol in your message, followed by +that person's name, and leaving a space after the name. + +All participants are: {agent_names} +""" def main() -> None: @@ -28,7 +36,9 @@ def main() -> None: hint = Msg( name="Host", - content=DEFAULT_TOPIC, + content=DEFAULT_TOPIC.format( + agent_names=[agent.name for agent in agents], + ), ) rnd = 0 @@ -46,7 +56,7 @@ def main() -> None: f"{USER_TIME_TO_SPEAK} seconds, skip.", ) - speak_list += filter_names(x.get("content", ""), npc_agents) + speak_list += filter_agents(x.get("content", ""), npc_agents) if len(speak_list) > 0: next_agent = speak_list.pop(0) @@ -55,7 +65,7 @@ def main() -> None: next_agent = select_next_one(npc_agents, rnd) x = next_agent() - speak_list += filter_names(x.content, npc_agents) + speak_list += filter_agents(x.content, npc_agents) rnd += 1 From 9819027b5039f80764a2407420e5f4d66d68f19e Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:36:41 +0800 Subject: [PATCH 09/14] update README --- examples/groupchat/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index 754b5b905..924508dbe 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -19,6 +19,9 @@ To start the group conversation, follow these steps: ```bash python main.py + +# or launch webui +as_studio main.py ``` 1. To address a specific agent in the chat, type "@" followed by the agent's name in your message. From 4062aedf11844d5f5e9af5253e3755fcd484d17e Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:45:03 +0800 Subject: [PATCH 10/14] revert irr fix --- src/agentscope/agents/dict_dialog_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agentscope/agents/dict_dialog_agent.py b/src/agentscope/agents/dict_dialog_agent.py index eb25b2fd8..be376105a 100644 --- a/src/agentscope/agents/dict_dialog_agent.py +++ b/src/agentscope/agents/dict_dialog_agent.py @@ -145,7 +145,7 @@ def reply(self, x: dict = None) -> dict: ).raw # logging raw messages in debug mode - logger.debug(json.dumps(response, ensure_ascii=False, indent=4)) + logger.debug(json.dumps(response, indent=4)) # In this agent, if the response is a dict, we treat "speak" as a # special key, which represents the text to be spoken From dd82b5b52a43065658d3d9bfe77f197cb787a466 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:49:14 +0800 Subject: [PATCH 11/14] update README --- examples/groupchat/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index 924508dbe..023cf6467 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -63,16 +63,16 @@ The `DEFAULT_TOPIC` variable defines the initial message or topic of the chat ro To customize this message, modify the `DEFAULT_TOPIC` variable in the `main.py` script. For instance, if you want to set the default topic to discuss "The Future of Artificial Intelligence," you would change the code as follows: ```python -DEFAULT_TOPIC = "Let's discuss the future of Artificial Intelligence." -``` +DEFAULT_TOPIC = """ +This is a chat room about the Future of Artificial Intelligence and you can +speak freely and briefly. -Remember to update the hint message sent to the `msghub` accordingly: +You can designate a member to reply to your message, you can use the @ symbol. +This means including the @ symbol in your message, followed by +that person's name, and leaving a space after the name. -```python -hint = Msg( - name="Host", - content=DEFAULT_TOPIC, -) +All participants are: {agent_names} +""" ``` With these customizations, the chat room can be tailored to fit specific themes or time constraints, enhancing the user's control over the chat experience. From 2883577caa9f894b9672253a00b35f74d275e425 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:51:46 +0800 Subject: [PATCH 12/14] update README --- examples/groupchat/README.md | 6 ------ examples/groupchat/main.py | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index 023cf6467..cc95d9674 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -66,12 +66,6 @@ To customize this message, modify the `DEFAULT_TOPIC` variable in the `main.py` DEFAULT_TOPIC = """ This is a chat room about the Future of Artificial Intelligence and you can speak freely and briefly. - -You can designate a member to reply to your message, you can use the @ symbol. -This means including the @ symbol in your message, followed by -that person's name, and leaving a space after the name. - -All participants are: {agent_names} """ ``` diff --git a/examples/groupchat/main.py b/examples/groupchat/main.py index f6e2f5784..0847c1979 100644 --- a/examples/groupchat/main.py +++ b/examples/groupchat/main.py @@ -14,7 +14,9 @@ USER_TIME_TO_SPEAK = 10 DEFAULT_TOPIC = """ This is a chat room and you can speak freely and briefly. +""" +SYS_PROMPT = """ You can designate a member to reply to your message, you can use the @ symbol. This means including the @ symbol in your message, followed by that person's name, and leaving a space after the name. @@ -36,7 +38,8 @@ def main() -> None: hint = Msg( name="Host", - content=DEFAULT_TOPIC.format( + content=DEFAULT_TOPIC + + SYS_PROMPT.format( agent_names=[agent.name for agent in agents], ), ) From 885f4744f50561f1029c465f8c1b96b11086dc58 Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Wed, 13 Mar 2024 19:54:00 +0800 Subject: [PATCH 13/14] update README --- examples/groupchat/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/groupchat/README.md b/examples/groupchat/README.md index cc95d9674..6359b3413 100644 --- a/examples/groupchat/README.md +++ b/examples/groupchat/README.md @@ -20,7 +20,7 @@ To start the group conversation, follow these steps: ```bash python main.py -# or launch webui +# or launch agentscope studio as_studio main.py ``` @@ -34,10 +34,11 @@ The conversation takes place in a simulated chat room environment with roles def ### Example Interaction ``` -User: Hi, everyone! I'm excited to join this chat. +User input: Hi, everyone! I'm excited to join this chat. AgentA: Welcome! We're glad to have you here. -User: @AgentB, what do you think about the new technology trends? +User input: @AgentB, what do you think about the new technology trends? AgentB: It's an exciting time for tech! There are so many innovations on the horizon. +... ``` ## Customization Options From 01ff3802c37115320abeccbb19e051c65efe56fa Mon Sep 17 00:00:00 2001 From: "weirui.kwr@alibaba-inc.com" Date: Thu, 14 Mar 2024 21:09:42 +0800 Subject: [PATCH 14/14] pass format --- examples/distributed/user_proxy_agent.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/distributed/user_proxy_agent.py b/examples/distributed/user_proxy_agent.py index 6c47bac23..a3437de6a 100644 --- a/examples/distributed/user_proxy_agent.py +++ b/examples/distributed/user_proxy_agent.py @@ -9,16 +9,22 @@ class UserProxyAgent(UserAgent): """User proxy agent class""" - def reply( + def reply( # type: ignore [override] self, x: dict = None, required_keys: Optional[Union[list[str], str]] = None, ) -> dict: + """ + Reply with `self.speak(x)` + """ if x is not None: self.speak(x) return super().reply(x, required_keys) def observe(self, x: Union[dict, Sequence[dict]]) -> None: + """ + Observe with `self.speak(x)` + """ if x is not None: self.speak(x) # type: ignore[arg-type] self.memory.add(x)