From e81355ef76aac9aa2fa8d347eeec2a97ffddb25f Mon Sep 17 00:00:00 2001 From: Nicolas Marchildon Date: Wed, 18 Oct 2023 12:19:02 -0400 Subject: [PATCH 1/7] Enable defining new functions after agent creation --- autogen/agentchat/conversable_agent.py | 12 +++++++ test/test_with_openai.py | 45 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 87845f910d17..38591a6af3a2 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1017,3 +1017,15 @@ def register_function(self, function_map: Dict[str, Callable]): function_map: a dictionary mapping function names to functions. """ self._function_map.update(function_map) + + def define_function(self, signature: Dict): + """Define a function in the LLM configuration. + + Args: + signature: description of the function to provide to the model. See: https://platform.openai.com/docs/api-reference/chat/create#chat/create-functions + """ + if not self.llm_config: + error_msg = "To define a function, agent must have an llm_config" + logger.error(error_msg) + raise AssertionError(error_msg) + self.llm_config["functions"].append(signature) diff --git a/test/test_with_openai.py b/test/test_with_openai.py index ed63324be6af..56eb7d8b8c3d 100644 --- a/test/test_with_openai.py +++ b/test/test_with_openai.py @@ -57,5 +57,50 @@ def get_random_number(): user_proxy.initiate_chat(manager, message="Let's start the game!") +@pytest.mark.skipif( + skip or not sys.version.startswith("3.10"), + reason="do not run if openai is not installed or py!=3.10", +) +def test_define_function(): + config_list_gpt4 = autogen.config_list_from_json( + "OAI_CONFIG_LIST", + filter_dict={ + "model": ["gpt-4", "gpt-4-0314", "gpt4", "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-v0314"], + }, + ) + llm_config = { + "config_list": config_list_gpt4, + "seed": 42, + "functions": [], + } + + user_proxy = autogen.UserProxyAgent(name="user_proxy", human_input_mode="NEVER") + assistant = autogen.AssistantAgent(name="test", llm_config=llm_config) + + # Define a new function *after* the assistant has been created + assistant.define_function( + { + "name": "greet_user", + "description": "Greets the user.", + "parameters": { + "type": "object", + "properties": {}, + "required": [], + }, + } + ) + + user_proxy.initiate_chat( + assistant, + message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", + ) + messages = assistant.chat_messages[user_proxy] + print(messages) + + # The model should know about the function in the context of the conversation + assert "greet_user" in messages[1]["content"] + + if __name__ == "__main__": test_function_call_groupchat() + test_define_function() From 79dfeb3c9d5e0fe58989eb50279d93c3e4045fbc Mon Sep 17 00:00:00 2001 From: Nicolas Marchildon Date: Wed, 18 Oct 2023 22:06:32 -0400 Subject: [PATCH 2/7] Add notebook for function inception example --- notebook/agentchat_inception_function.ipynb | 325 ++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 notebook/agentchat_inception_function.ipynb diff --git a/notebook/agentchat_inception_function.ipynb b/notebook/agentchat_inception_function.ipynb new file mode 100644 index 000000000000..9f5c9c98aae4 --- /dev/null +++ b/notebook/agentchat_inception_function.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function Inception\n", + "\n", + "AutoGen offers conversable agents powered by LLM, tool or human, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participation through multi-agent conversation. Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n", + "\n", + "In this notebook, we demonstrate how to use `AssistantAgent` and `UserProxyAgent` to give them the ability to auto-extend the list of functions the model may call. Functions need to be registered to `UserProxyAgent`, which will be responsible for executing any function calls made by `AssistantAgent`. The assistant also needs to know the signature of functions that may be called. A special `define_function` function is registered, which registers a new function in `UserProxyAgent` and updates the configuration of the assistant.\n", + "\n", + "In the example scenario, the user first asks to define a function that gets a URL and prints the response body. Then the user asks to print the response body, and the assistant suggests to the user to call the new function.\n", + "\n", + "## Requirements\n", + "\n", + "AutoGen requires `Python>=3.8`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", + "\n", + "Define a function that gets a URL, then prints the response body.\n", + "Reply TERMINATE when the function is defined.\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", + "\n", + "\u001b[32m***** Suggested function Call: define_function *****\u001b[0m\n", + "Arguments: \n", + "{\n", + " \"name\": \"get_url_response_body\",\n", + " \"description\": \"Gets a URL, then prints the response body\",\n", + " \"arguments\": \"{\\\"url\\\": {\\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"The URL\\\"}}\",\n", + " \"packages\": \"requests\",\n", + " \"code\": \"import requests\\n\\n\\ndef get_url_response_body(url):\\n response = requests.get(url)\\n print(response.text)\\n\"\n", + "}\n", + "\u001b[32m****************************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[35m\n", + ">>>>>>>> EXECUTING FUNCTION define_function...\u001b[0m\n", + "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", + "\n", + "\u001b[32m***** Response from calling function \"define_function\" *****\u001b[0m\n", + "A function has been added to the context of this conversation.\n", + "Description: Gets a URL, then prints the response body\n", + "\u001b[32m************************************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", + "\n", + "Print the response body of https://echo.free.beeceptor.com/\n", + "Use the functions you know about.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", + "\n", + "\u001b[32m***** Suggested function Call: get_url_response_body *****\u001b[0m\n", + "Arguments: \n", + "{\n", + " \"url\": \"https://echo.free.beeceptor.com/\"\n", + "}\n", + "\u001b[32m**********************************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[35m\n", + ">>>>>>>> EXECUTING FUNCTION get_url_response_body...\u001b[0m\n", + "execute_code:\n", + "\n", + "import subprocess\n", + "print(\"Installing package: requests\")\n", + "subprocess.run([\"pip\", \"-qq\", \"install\", \"requests\"])\n", + "print(\"Result of get_url_response_body function execution:\")\n", + "import requests\n", + "\n", + "\n", + "def get_url_response_body(url):\n", + " response = requests.get(url)\n", + " print(response.text)\n", + "\n", + "args={'url': 'https://echo.free.beeceptor.com/'}\n", + "result=get_url_response_body(**args)\n", + "if result is not None: print(result)\n", + "\n", + "Result: Installing package: requests\n", + "Result of get_url_response_body function execution:\n", + "{\n", + " \"method\": \"GET\",\n", + " \"path\": \"/\",\n", + " \"ip\": \"104.28.208.116\",\n", + " \"headers\": {\n", + " \"host\": \"echo.free.beeceptor.com\",\n", + " \"user-agent\": \"python-requests/2.31.0\",\n", + " \"accept\": \"*/*\",\n", + " \"accept-encoding\": \"gzip, deflate\",\n", + " \"x-forwarded-for\": \"104.28.208.116\",\n", + " \"x-forwarded-host\": \"echo.free.beeceptor.com\",\n", + " \"x-forwarded-proto\": \"https\",\n", + " \"x-real-ip\": \"104.28.208.116\"\n", + " },\n", + " \"parsedQueryParams\": {}\n", + "}\n", + "\n", + "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", + "\n", + "\u001b[32m***** Response from calling function \"get_url_response_body\" *****\u001b[0m\n", + "Installing package: requests\n", + "Result of get_url_response_body function execution:\n", + "{\n", + " \"method\": \"GET\",\n", + " \"path\": \"/\",\n", + " \"ip\": \"104.28.208.116\",\n", + " \"headers\": {\n", + " \"host\": \"echo.free.beeceptor.com\",\n", + " \"user-agent\": \"python-requests/2.31.0\",\n", + " \"accept\": \"*/*\",\n", + " \"accept-encoding\": \"gzip, deflate\",\n", + " \"x-forwarded-for\": \"104.28.208.116\",\n", + " \"x-forwarded-host\": \"echo.free.beeceptor.com\",\n", + " \"x-forwarded-proto\": \"https\",\n", + " \"x-real-ip\": \"104.28.208.116\"\n", + " },\n", + " \"parsedQueryParams\": {}\n", + "}\n", + "\n", + "\u001b[32m******************************************************************\u001b[0m\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", + "\n", + "The response body of https://echo.free.beeceptor.com/ is:\n", + "\n", + "{\n", + " \"method\": \"GET\",\n", + " \"path\": \"/\",\n", + " \"ip\": \"104.28.208.116\",\n", + " \"headers\": {\n", + " \"host\": \"echo.free.beeceptor.com\",\n", + " \"user-agent\": \"python-requests/2.31.0\",\n", + " \"accept\": \"*/*\",\n", + " \"accept-encoding\": \"gzip, deflate\",\n", + " \"x-forwarded-for\": \"104.28.208.116\",\n", + " \"x-forwarded-host\": \"echo.free.beeceptor.com\",\n", + " \"x-forwarded-proto\": \"https\",\n", + " \"x-real-ip\": \"104.28.208.116\"\n", + " },\n", + " \"parsedQueryParams\": {}\n", + "}\n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "from autogen import AssistantAgent, UserProxyAgent, config_list_from_json\n", + "from autogen.code_utils import execute_code\n", + "import json\n", + "\n", + "config_list = config_list_from_json(\n", + " \"OAI_CONFIG_LIST\",\n", + " filter_dict={\n", + " # Function calling with GPT 3.5\n", + " \"model\": [\"gpt-3.5-turbo-16k-0613\"],\n", + " },\n", + ")\n", + "llm_config = {\n", + " \"functions\": [\n", + " {\n", + " \"name\": \"define_function\",\n", + " \"description\": \"Define a function to add to the context of the conversation. Necessary Python packages must be declared. Once defined, the assistant may decide to use this function, respond with a normal message.\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"name\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The name of the function to define.\",\n", + " },\n", + " \"description\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"A short description of the function.\",\n", + " },\n", + " \"arguments\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"JSON schema of arguments encoded as a string. For example: { \\\"url\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"The URL\\\", }}\",\n", + " },\n", + " \"packages\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"A list of package names imported by the function, and that need to be installed with pip prior to invoking the function. This solves ModuleNotFoundError.\",\n", + " },\n", + " \"code\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The implementation in Python. Do not include the function declaration.\",\n", + " },\n", + " },\n", + " \"required\": [\"name\", \"description\", \"arguments\", \"packages\", \"code\"],\n", + " },\n", + " },\n", + " ],\n", + " \"config_list\": config_list,\n", + " \"request_timeout\": 120,\n", + "}\n", + "\n", + "def define_function(name, description, arguments, packages, code):\n", + " json_args = json.loads(arguments)\n", + " function_config = {\n", + " \"name\": name,\n", + " \"description\": description,\n", + " \"parameters\": { \"type\": \"object\", \"properties\": json_args },\n", + " # TODO Make all arguments required\n", + " \"required\": [\"url\"],\n", + " }\n", + " llm_config[\"functions\"] = llm_config[\"functions\"] + [function_config]\n", + " user_proxy.register_function(\n", + " function_map={\n", + " name: lambda **args: execute_func(name, packages, code, **args)\n", + " }\n", + " )\n", + " assistant.define_function(function_config)\n", + " return f\"A function has been added to the context of this conversation.\\nDescription: {description}\"\n", + "\n", + "def execute_func(name, packages, code, **args):\n", + " pip_install = f\"\"\"print(\"Installing package: {packages}\")\\nsubprocess.run([\"pip\", \"-qq\", \"install\", \"{packages}\"])\"\"\" if packages else ''\n", + " str = f\"\"\"\n", + "import subprocess\n", + "{pip_install}\n", + "print(\"Result of {name} function execution:\")\n", + "{code}\n", + "args={args}\n", + "result={name}(**args)\n", + "if result is not None: print(result)\n", + "\"\"\"\n", + " print(f\"execute_code:\\n{str}\")\n", + " result = execute_code(str)[1]\n", + " print(f\"Result: {result}\")\n", + " return result\n", + "\n", + "def _is_termination_msg(message):\n", + " \"\"\"Check if a message is a termination message.\"\"\"\n", + " if isinstance(message, dict):\n", + " message = message.get(\"content\")\n", + " if message is None:\n", + " return False\n", + " return message.rstrip().endswith(\"TERMINATE\")\n", + "\n", + "assistant = AssistantAgent(\n", + " name=\"chatbot\",\n", + " system_message=\"\"\"You are an assistant.\n", + " The user will ask a question.\n", + " You may use the provided functions before providing a final answer.\n", + " Only use the functions you were provided.\n", + " When the answer has been provided, reply TERMINATE.\"\"\",\n", + " llm_config=llm_config,\n", + ")\n", + "\n", + "user_proxy = UserProxyAgent(\n", + " \"user_proxy\",\n", + " code_execution_config=False,\n", + " is_termination_msg=_is_termination_msg,\n", + " default_auto_reply=\"Reply TERMINATE when the initial request has been fulfilled.\",\n", + " human_input_mode=\"NEVER\")\n", + "\n", + "user_proxy.register_function(\n", + " function_map={\n", + " \"define_function\": define_function\n", + " }\n", + ")\n", + "\n", + "# user_proxy.initiate_chat(\n", + "# assistant, message=\"What functions do you know about?\")\n", + "\n", + "user_proxy.initiate_chat(\n", + " assistant, message=\"Define a function that gets a URL, then prints the response body.\\nReply TERMINATE when the function is defined.\")\n", + "\n", + "# user_proxy.initiate_chat(\n", + "# assistant, message=\"List functions do you know about.\")\n", + "\n", + "user_proxy.initiate_chat(\n", + " assistant, message=\"Print the response body of https://echo.free.beeceptor.com/\\nUse the functions you know about.\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From d58b451b32c3493f649735cda1398fb2609503c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cskzhang1=E2=80=9D?= <“shaokunzhang529@gmail.com”> Date: Wed, 8 Nov 2023 10:02:12 -0500 Subject: [PATCH 3/7] format --- autogen/agentchat/conversable_agent.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 68755427f8c2..804766ef4578 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1204,5 +1204,3 @@ def can_execute_function(self, name: str) -> bool: def function_map(self) -> Dict[str, Callable]: """Return the function map.""" return self._function_map - - From c95aae18af51df8656fd2def8b2f09ffa0cf6088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cskzhang1=E2=80=9D?= <“shaokunzhang529@gmail.com”> Date: Fri, 1 Dec 2023 15:13:50 -0500 Subject: [PATCH 4/7] 1. fix bug 2. support remove function --- .../agentchat/test_function_call_groupchat.py | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/test/agentchat/test_function_call_groupchat.py b/test/agentchat/test_function_call_groupchat.py index 16036390ed12..f761acade23c 100644 --- a/test/agentchat/test_function_call_groupchat.py +++ b/test/agentchat/test_function_call_groupchat.py @@ -63,7 +63,7 @@ def get_random_number(): skip or not sys.version.startswith("3.10"), reason="do not run if openai is not installed or py!=3.10", ) -def test_define_function(): +def test_update_function(): config_list_gpt4 = autogen.config_list_from_json( "OAI_CONFIG_LIST", filter_dict={ @@ -76,11 +76,15 @@ def test_define_function(): "functions": [], } - user_proxy = autogen.UserProxyAgent(name="user_proxy", human_input_mode="NEVER") + 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, + ) assistant = autogen.AssistantAgent(name="test", llm_config=llm_config) # Define a new function *after* the assistant has been created - assistant.define_function( + assistant.update_function_signature( { "name": "greet_user", "description": "Greets the user.", @@ -89,20 +93,28 @@ def test_define_function(): "properties": {}, "required": [], }, - } + }, + is_remove=False, ) - user_proxy.initiate_chat( assistant, message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", ) - messages = assistant.chat_messages[user_proxy] - print(messages) + messages1 = assistant.chat_messages[user_proxy][-1]["content"] + print(messages1) + assistant.update_function_signature("greet_user", is_remove=True) + user_proxy.initiate_chat( + assistant, + message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", + ) + messages2 = assistant.chat_messages[user_proxy][-1]["content"] + print(messages2) # The model should know about the function in the context of the conversation - assert "greet_user" in messages[1]["content"] + assert "greet_user" in messages1 + assert "greet_user" not in messages2 if __name__ == "__main__": test_function_call_groupchat() - test_define_function() + test_update_function() From 2e1c5aaadd318ff0bd22961cf1cca914a3b50dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cskzhang1=E2=80=9D?= <“shaokunzhang529@gmail.com”> Date: Fri, 1 Dec 2023 15:14:27 -0500 Subject: [PATCH 5/7] 1. fix bug 2. support remove function --- autogen/agentchat/conversable_agent.py | 33 +++++++++++++++++---- notebook/agentchat_inception_function.ipynb | 2 +- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 86b7a53195bf..0734490e9510 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1188,17 +1188,40 @@ def register_function(self, function_map: Dict[str, Callable]): """ self._function_map.update(function_map) - def define_function(self, signature: Dict): - """Define a function in the LLM configuration. + def update_function_signature(self, func_sig: Union[str, Dict], is_remove: None): + """update a function_signature in the LLM configuration for function_call. Args: - signature: description of the function to provide to the model. See: https://platform.openai.com/docs/api-reference/chat/create#chat/create-functions + func_sig (str or dict): description/name of the function to update/remove to the model. See: https://platform.openai.com/docs/api-reference/chat/create#chat/create-functions + is_remove: whether removing the funciton from llm_config with name 'func_sig' """ + if not self.llm_config: - error_msg = "To define a function, agent must have an llm_config" + error_msg = "To update a function signature, agent must have an llm_config" logger.error(error_msg) raise AssertionError(error_msg) - self.llm_config["functions"].append(signature) + + if is_remove: + if "functions" not in self.llm_config.keys(): + error_msg = "The agent config doesn't have function {name}.".format(name=func_sig) + logger.error(error_msg) + raise AssertionError(error_msg) + else: + self.llm_config["functions"] = [ + func for func in self.llm_config["functions"] if func["name"] != func_sig + ] + else: + if "functions" in self.llm_config.keys(): + self.llm_config["functions"] = [ + func for func in self.llm_config["functions"] if func.get("name") != func_sig["name"] + ] + [func_sig] + else: + self.llm_config["functions"] = [func_sig] + + if len(self.llm_config["functions"]) == 0: + del self.llm_config["functions"] + + self.client = OpenAIWrapper(**self.llm_config) def can_execute_function(self, name: str) -> bool: """Whether the agent can execute the function.""" diff --git a/notebook/agentchat_inception_function.ipynb b/notebook/agentchat_inception_function.ipynb index 9f5c9c98aae4..78b4ab1b87d4 100644 --- a/notebook/agentchat_inception_function.ipynb +++ b/notebook/agentchat_inception_function.ipynb @@ -237,7 +237,7 @@ " name: lambda **args: execute_func(name, packages, code, **args)\n", " }\n", " )\n", - " assistant.define_function(function_config)\n", + " assistant.update_function_signature(function_config, is_remove= False)\n", " return f\"A function has been added to the context of this conversation.\\nDescription: {description}\"\n", "\n", "def execute_func(name, packages, code, **args):\n", From 03108cf834b890203253e9499317ca508b48790f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cskzhang1=E2=80=9D?= <“shaokunzhang529@gmail.com”> Date: Fri, 1 Dec 2023 17:11:29 -0500 Subject: [PATCH 6/7] 1. add example doc 2. change test file 3. change ipynb title --- notebook/agentchat_inception_function.ipynb | 2 +- test/agentchat/test_function_call.py | 58 +++++++++++++++++++ .../agentchat/test_function_call_groupchat.py | 57 ------------------ website/docs/Examples.md | 1 + 4 files changed, 60 insertions(+), 58 deletions(-) diff --git a/notebook/agentchat_inception_function.ipynb b/notebook/agentchat_inception_function.ipynb index 78b4ab1b87d4..1a3bfb8c86b1 100644 --- a/notebook/agentchat_inception_function.ipynb +++ b/notebook/agentchat_inception_function.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Function Inception\n", + "# Auto Generated Agent Chat: Function Inception\n", "\n", "AutoGen offers conversable agents powered by LLM, tool or human, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participation through multi-agent conversation. Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n", "\n", diff --git a/test/agentchat/test_function_call.py b/test/agentchat/test_function_call.py index ef2ad5cc3ee3..0a72c0925c18 100644 --- a/test/agentchat/test_function_call.py +++ b/test/agentchat/test_function_call.py @@ -8,6 +8,7 @@ import autogen from autogen.math_utils import eval_math_responses from test_assistant_agent import KEY_LOC +import sys @pytest.mark.skipif(OpenAI is None, reason="openai>=1 not installed") @@ -188,8 +189,65 @@ def get_number(): assert (await user.a_execute_function(func_call))[1]["content"] == "42" +@pytest.mark.skipif( + not OpenAI or not sys.version.startswith("3.10"), + reason="do not run if openai is not installed or py!=3.10", +) +def test_update_function(): + config_list_gpt4 = autogen.config_list_from_json( + "OAI_CONFIG_LIST", + filter_dict={ + "model": ["gpt-4", "gpt-4-0314", "gpt4", "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-v0314"], + }, + ) + llm_config = { + "config_list": config_list_gpt4, + "seed": 42, + "functions": [], + } + + 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, + ) + assistant = autogen.AssistantAgent(name="test", llm_config=llm_config) + + # Define a new function *after* the assistant has been created + assistant.update_function_signature( + { + "name": "greet_user", + "description": "Greets the user.", + "parameters": { + "type": "object", + "properties": {}, + "required": [], + }, + }, + is_remove=False, + ) + user_proxy.initiate_chat( + assistant, + message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", + ) + messages1 = assistant.chat_messages[user_proxy][-1]["content"] + print(messages1) + + assistant.update_function_signature("greet_user", is_remove=True) + user_proxy.initiate_chat( + assistant, + message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", + ) + messages2 = assistant.chat_messages[user_proxy][-1]["content"] + print(messages2) + # The model should know about the function in the context of the conversation + assert "greet_user" in messages1 + assert "greet_user" not in messages2 + + if __name__ == "__main__": # test_json_extraction() # test_execute_function() + test_update_function() asyncio.run(test_a_execute_function()) test_eval_math_responses() diff --git a/test/agentchat/test_function_call_groupchat.py b/test/agentchat/test_function_call_groupchat.py index f761acade23c..484149035621 100644 --- a/test/agentchat/test_function_call_groupchat.py +++ b/test/agentchat/test_function_call_groupchat.py @@ -59,62 +59,5 @@ def get_random_number(): user_proxy.initiate_chat(manager, message="Let's start the game!") -@pytest.mark.skipif( - skip or not sys.version.startswith("3.10"), - reason="do not run if openai is not installed or py!=3.10", -) -def test_update_function(): - config_list_gpt4 = autogen.config_list_from_json( - "OAI_CONFIG_LIST", - filter_dict={ - "model": ["gpt-4", "gpt-4-0314", "gpt4", "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-v0314"], - }, - ) - llm_config = { - "config_list": config_list_gpt4, - "seed": 42, - "functions": [], - } - - 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, - ) - assistant = autogen.AssistantAgent(name="test", llm_config=llm_config) - - # Define a new function *after* the assistant has been created - assistant.update_function_signature( - { - "name": "greet_user", - "description": "Greets the user.", - "parameters": { - "type": "object", - "properties": {}, - "required": [], - }, - }, - is_remove=False, - ) - user_proxy.initiate_chat( - assistant, - message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", - ) - messages1 = assistant.chat_messages[user_proxy][-1]["content"] - print(messages1) - - assistant.update_function_signature("greet_user", is_remove=True) - user_proxy.initiate_chat( - assistant, - message="What functions do you know about in the context of this conversation? End your response with 'TERMINATE'.", - ) - messages2 = assistant.chat_messages[user_proxy][-1]["content"] - print(messages2) - # The model should know about the function in the context of the conversation - assert "greet_user" in messages1 - assert "greet_user" not in messages2 - - if __name__ == "__main__": test_function_call_groupchat() - test_update_function() diff --git a/website/docs/Examples.md b/website/docs/Examples.md index a77c48b8c575..1a98948f0da6 100644 --- a/website/docs/Examples.md +++ b/website/docs/Examples.md @@ -39,6 +39,7 @@ Links to notebook examples: - Task Solving with Langchain Provided Tools as Functions - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_langchain.ipynb) - **RAG**: Group Chat with Retrieval Augmented Generation (with 5 group member agents and 1 manager agent) - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_groupchat_RAG.ipynb) - In-depth Guide to OpenAI Utility Functions - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/oai_openai_utils.ipynb) + - Function Inception, enables autogen agents to update/remove functions in the conversation. - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_inception_function.ipynb) 5. **Agent Teaching and Learning** - Teach Agents New Skills & Reuse via Automated Chat - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_teaching.ipynb) From d9a20e8f107a58e3b69b99492b25a4d2064d2969 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Fri, 1 Dec 2023 19:58:07 -0500 Subject: [PATCH 7/7] Update website/docs/Examples.md --- website/docs/Examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/Examples.md b/website/docs/Examples.md index 1a98948f0da6..b338e533f597 100644 --- a/website/docs/Examples.md +++ b/website/docs/Examples.md @@ -39,7 +39,7 @@ Links to notebook examples: - Task Solving with Langchain Provided Tools as Functions - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_langchain.ipynb) - **RAG**: Group Chat with Retrieval Augmented Generation (with 5 group member agents and 1 manager agent) - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_groupchat_RAG.ipynb) - In-depth Guide to OpenAI Utility Functions - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/oai_openai_utils.ipynb) - - Function Inception, enables autogen agents to update/remove functions in the conversation. - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_inception_function.ipynb) + - Function Inception: Enable AutoGen agents to update/remove functions during conversations. - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_inception_function.ipynb) 5. **Agent Teaching and Learning** - Teach Agents New Skills & Reuse via Automated Chat - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_teaching.ipynb)