From 7e589a1bbbbebeee012e03ae1e5c27efc2fb1fa9 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Wed, 27 Nov 2024 10:48:39 -0800 Subject: [PATCH 1/2] User proxy documentation and fixes (#4401) * Fix handoff bug in user proxy agent * Update documentation --------- --- .../agents/_user_proxy_agent.py | 40 ++++++++++++++++--- .../tests/test_userproxy_agent.py | 17 ++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_user_proxy_agent.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_user_proxy_agent.py index bdaca53ddc6c..5de17f706431 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_user_proxy_agent.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_user_proxy_agent.py @@ -15,12 +15,38 @@ class UserProxyAgent(BaseChatAgent): - """An agent that can represent a human user in a chat.""" + """An agent that can represent a human user through an input function. + + This agent can be used to represent a human user in a chat system by providing a custom input function. + + Args: + name (str): The name of the agent. + description (str, optional): A description of the agent. + input_func (Optional[Callable[[str], str]], Callable[[str, Optional[CancellationToken]], Awaitable[str]]): A function that takes a prompt and returns a user input string. + + .. note:: + + Using :class:`UserProxyAgent` puts a running team in a temporary blocked + state until the user responds. So it is important to time out the user input + function and cancel using the :class:`~autogen_core.base.CancellationToken` if the user does not respond. + The input function should also handle exceptions and return a default response if needed. + + For typical use cases that involve + slow human responses, it is recommended to use termination conditions + such as :class:`~autogen_agentchat.task.HandoffTermination` or :class:`~autogen_agentchat.task.SourceMatchTermination` + to stop the running team and return the control to the application. + You can run the team again with the user input. This way, the state of the team + can be saved and restored when the user responds. + + See `Pause for User Input `_ for more information. + + """ def __init__( self, name: str, - description: str = "a human user", + *, + description: str = "A human user", input_func: Optional[InputFuncType] = None, ) -> None: """Initialize the UserProxyAgent.""" @@ -34,10 +60,12 @@ def produced_message_types(self) -> List[type[ChatMessage]]: return [TextMessage, HandoffMessage] def _get_latest_handoff(self, messages: Sequence[ChatMessage]) -> Optional[HandoffMessage]: - """Find the most recent HandoffMessage in the message sequence.""" - for message in reversed(messages): - if isinstance(message, HandoffMessage): - return message + """Find the HandoffMessage in the message sequence that addresses this agent.""" + if len(messages) > 0 and isinstance(messages[-1], HandoffMessage): + if messages[-1].target == self.name: + return messages[-1] + else: + raise RuntimeError(f"Handoff message target does not match agent name: {messages[-1].source}") return None async def _get_input(self, prompt: str, cancellation_token: Optional[CancellationToken]) -> str: diff --git a/python/packages/autogen-agentchat/tests/test_userproxy_agent.py b/python/packages/autogen-agentchat/tests/test_userproxy_agent.py index 2ef3053f09bf..d06e209cfbbc 100644 --- a/python/packages/autogen-agentchat/tests/test_userproxy_agent.py +++ b/python/packages/autogen-agentchat/tests/test_userproxy_agent.py @@ -65,6 +65,23 @@ def custom_input(prompt: str) -> str: assert response.chat_message.source == "test_user" assert response.chat_message.target == "assistant" + # The latest message if is a handoff message, it must be addressed to this agent. + messages = [ + TextMessage(content="Initial message", source="assistant"), + HandoffMessage(content="Handing off to user for confirmation", source="assistant", target="other_agent"), + ] + with pytest.raises(RuntimeError): + await agent.on_messages(messages, CancellationToken()) + + # No handoff message if the latest message is not a handoff message addressed to this agent. + messages = [ + TextMessage(content="Initial message", source="assistant"), + HandoffMessage(content="Handing off to other agent", source="assistant", target="other_agent"), + TextMessage(content="Another message", source="other_agent"), + ] + response = await agent.on_messages(messages, CancellationToken()) + assert isinstance(response.chat_message, TextMessage) + @pytest.mark.asyncio async def test_cancellation() -> None: From 7c8d25c4486e21deb0e45a46b7ee0f4219a7d535 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 11:56:00 -0800 Subject: [PATCH 2/2] remove dep on ELSA workflows - unused. (#4393) --- dotnet/Directory.Packages.props | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 9bde32e6d012..1014669a9397 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -24,15 +24,6 @@ - - - - - - - - -