From 63a35e79f881f45d43f8d9f087c8b8a7d40ddf56 Mon Sep 17 00:00:00 2001 From: Brian Finney Date: Sun, 14 Jan 2024 18:34:27 -0800 Subject: [PATCH] run sync and async in async (#1242) * run sync and async in async * test for async multitool call * uncomment notebook --------- Co-authored-by: Chi Wang Co-authored-by: Davor Runje --- autogen/agentchat/conversable_agent.py | 4 +- notebook/agentchat_function_call_async.ipynb | 107 +++++++++++++++---- test/agentchat/test_tool_calls.py | 103 ++++++++++++++++++ 3 files changed, 190 insertions(+), 24 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index c1c62e64e..0a94905ff 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -950,9 +950,7 @@ class ConversableAgent(Agent): message = messages[-1] async_tool_calls = [] for tool_call in message.get("tool_calls", []): - func = self._function_map.get(tool_call.get("function", {}).get("name", None), None) - if func and asyncio.coroutines.iscoroutinefunction(func): - async_tool_calls.append(self._a_execute_tool_call(tool_call)) + async_tool_calls.append(self._a_execute_tool_call(tool_call)) if async_tool_calls: tool_returns = await asyncio.gather(*async_tool_calls) return True, { diff --git a/notebook/agentchat_function_call_async.ipynb b/notebook/agentchat_function_call_async.ipynb index 27f977a5b..e06c28af4 100644 --- a/notebook/agentchat_function_call_async.ipynb +++ b/notebook/agentchat_function_call_async.ipynb @@ -61,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "id": "dca301a4", "metadata": {}, "outputs": [], @@ -119,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 6, "id": "9fb85afb", "metadata": {}, "outputs": [ @@ -134,14 +134,22 @@ "--------------------------------------------------------------------------------\n", "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", "\n", - "\u001b[32m***** Suggested tool Call (call_thUjscBN349eGd6xh3XrVT18): timer *****\u001b[0m\n", + "\u001b[32m***** Suggested tool Call (call_mZqUtVEf6hwarnCqdiKtVhAE): timer *****\u001b[0m\n", "Arguments: \n", - "{\"num_seconds\":\"5\"}\n", + "{\n", + " \"num_seconds\": \"5\"\n", + "}\n", "\u001b[32m**********************************************************************\u001b[0m\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[35m\n", - ">>>>>>>> EXECUTING ASYNC FUNCTION timer...\u001b[0m\n", + ">>>>>>>> EXECUTING ASYNC FUNCTION timer...\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", "\n", "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", @@ -153,14 +161,16 @@ "--------------------------------------------------------------------------------\n", "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", "\n", - "\u001b[32m***** Suggested tool Call (call_ubo7cKE3TKumGHkqGjQtZisy): stopwatch *****\u001b[0m\n", + "\u001b[32m***** Suggested tool Call (call_xHz73PpH4ipdv7kTxK2AiVCf): stopwatch *****\u001b[0m\n", "Arguments: \n", - "{\"num_seconds\":\"5\"}\n", + "{\n", + " \"num_seconds\": \"5\"\n", + "}\n", "\u001b[32m**************************************************************************\u001b[0m\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[35m\n", - ">>>>>>>> EXECUTING FUNCTION stopwatch...\u001b[0m\n", + ">>>>>>>> EXECUTING ASYNC FUNCTION stopwatch...\u001b[0m\n", "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", "\n", "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", @@ -172,8 +182,6 @@ "--------------------------------------------------------------------------------\n", "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", "\n", - "Both the timer and the stopwatch for 5 seconds have been completed. \n", - "\n", "TERMINATE\n", "\n", "--------------------------------------------------------------------------------\n" @@ -245,7 +253,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 7, "id": "2472f95c", "metadata": {}, "outputs": [], @@ -280,20 +288,77 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, "id": "e2c9267a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\n", + "1) Create a timer for 5 seconds.\n", + "2) a stopwatch for 5 seconds.\n", + "3) Pretty print the result as md.\n", + "4) when 1-3 are done, terminate the group chat\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mchatbot\u001b[0m (to chat_manager):\n", + "\n", + "\u001b[32m***** Suggested tool Call (call_rdYofiAgFpRfaZaCT4tzAhOb): timer *****\u001b[0m\n", + "Arguments: \n", + "{\n", + " \"num_seconds\": \"5\"\n", + "}\n", + "\u001b[32m**********************************************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[35m\n", + ">>>>>>>> EXECUTING ASYNC FUNCTION timer...\u001b[0m\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\u001b[32m***** Response from calling tool \"timer\" *****\u001b[0m\n", + "Timer is done!\n", + "\u001b[32m**********************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chat_manager):\n", + "\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], "source": [ "# todo: remove comment after fixing https://github.com/microsoft/autogen/issues/1205\n", - "# await user_proxy.a_initiate_chat( # noqa: F704\n", - "# manager,\n", - "# message=\"\"\"\n", - "# 1) Create a timer for 5 seconds.\n", - "# 2) a stopwatch for 5 seconds.\n", - "# 3) Pretty print the result as md.\n", - "# 4) when 1-3 are done, terminate the group chat\"\"\",\n", - "# )" + "await user_proxy.a_initiate_chat( # noqa: F704\n", + " manager,\n", + " message=\"\"\"\n", + "1) Create a timer for 5 seconds.\n", + "2) a stopwatch for 5 seconds.\n", + "3) Pretty print the result as md.\n", + "4) when 1-3 are done, terminate the group chat\"\"\",\n", + ")" ] }, { diff --git a/test/agentchat/test_tool_calls.py b/test/agentchat/test_tool_calls.py index e9b236e31..4f4e822ea 100644 --- a/test/agentchat/test_tool_calls.py +++ b/test/agentchat/test_tool_calls.py @@ -278,6 +278,109 @@ def test_multi_tool_call(): ] +@pytest.mark.skipif(not TOOL_ENABLED, reason="openai>=1.1.0 not installed") +@pytest.mark.asyncio +async def test_async_multi_tool_call(): + class FakeAgent(autogen.Agent): + def __init__(self, name): + super().__init__(name) + self.received = [] + + async def a_receive( + self, + message, + sender, + request_reply=None, + silent=False, + ): + message = message if isinstance(message, list) else [message] + self.received.extend(message) + return "" + + user_proxy = autogen.UserProxyAgent( + name="user_proxy", + human_input_mode="NEVER", + is_termination_msg=lambda x: True if "TERMINATE" in x.get("content") else False, + ) + + def echo(str): + return str + + async def a_echo(str): + return str + + user_proxy.register_function({"a_echo": a_echo, "echo": echo}) + + fake_agent = FakeAgent("fake_agent") + + await user_proxy.a_receive( + message={ + "content": "test multi tool call", + "tool_calls": [ + { + "id": "tool_1", + "type": "function", + "function": {"name": "a_echo", "arguments": json.JSONEncoder().encode({"str": "hello world"})}, + }, + { + "id": "tool_2", + "type": "function", + "function": { + "name": "echo", + "arguments": json.JSONEncoder().encode({"str": "goodbye and thanks for all the fish"}), + }, + }, + { + "id": "tool_3", + "type": "function", + "function": { + "name": "multi_tool_call_echo", # normalized "multi_tool_call.echo" + "arguments": json.JSONEncoder().encode({"str": "goodbye and thanks for all the fish"}), + }, + }, + ], + }, + sender=fake_agent, + request_reply=True, + ) + + assert fake_agent.received == [ + { + "role": "tool", + "tool_responses": [ + {"tool_call_id": "tool_1", "role": "tool", "name": "a_echo", "content": "hello world"}, + { + "tool_call_id": "tool_2", + "role": "tool", + "name": "echo", + "content": "goodbye and thanks for all the fish", + }, + { + "tool_call_id": "tool_3", + "role": "tool", + "name": "multi_tool_call_echo", + "content": "Error: Function multi_tool_call_echo not found.", + }, + ], + "content": inspect.cleandoc( + """ + Tool call: a_echo + Id: tool_1 + hello world + + Tool call: echo + Id: tool_2 + goodbye and thanks for all the fish + + Tool call: multi_tool_call_echo + Id: tool_3 + Error: Function multi_tool_call_echo not found. + """ + ), + } + ] + + if __name__ == "__main__": # test_update_tool() # test_eval_math_responses()