From 215a1b2bfcefad0603c10c1234a5e73a822a0d77 Mon Sep 17 00:00:00 2001 From: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:24:09 -0800 Subject: [PATCH] Simple example --- .../multi-agent-collaboration.ipynb | 475 ++++++++++++++++++ examples/advanced_agents/multi-agent/plot.png | Bin 0 -> 21953 bytes 2 files changed, 475 insertions(+) create mode 100644 examples/advanced_agents/multi-agent/multi-agent-collaboration.ipynb create mode 100644 examples/advanced_agents/multi-agent/plot.png diff --git a/examples/advanced_agents/multi-agent/multi-agent-collaboration.ipynb b/examples/advanced_agents/multi-agent/multi-agent-collaboration.ipynb new file mode 100644 index 000000000..ade3e1609 --- /dev/null +++ b/examples/advanced_agents/multi-agent/multi-agent-collaboration.ipynb @@ -0,0 +1,475 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "39fd1948-b5c3-48c4-b10e-2ae7e8c83334", + "metadata": {}, + "source": [ + "# Multi-agent Collaboration Intro\n", + "\n", + "A single agent can usually operate effectively using a handful of tools and a scoped domain, but even using powerful models like `gpt-4`, it can be less effective at using many tools. One way to improve your system's performance is through multi-agent collaboration. Each agent can specialize in a task or domain, and LangGraph can effectively orchestrate them to accomplish a larger goal. \n", + "\n", + "This notebook is inspired by the paper [AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation](https://arxiv.org/abs/2308.08155), by Wu, et. al." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d7b6dcc-c985-46e2-8457-7e6b0298b950", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -U langchain langchain_openai langsmith pandas" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "743c19df-6da9-4d1e-b2d2-ea40080b9fdc", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_if_undefined(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass(f\"Please provide your {var}\")\n", + "\n", + "\n", + "_set_if_undefined(\"OPENAI_API_KEY\")\n", + "_set_if_undefined(\"LANGCHAIN_API_KEY\")\n", + "\n", + "# Optional, add tracing in LangSmith\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"Multi-agent Collaboration\"" + ] + }, + { + "cell_type": "markdown", + "id": "7d5fc0f3-5d9e-4e72-a281-177f101c2a7d", + "metadata": {}, + "source": [ + "## Example 1: 2 Agents\n", + "\n", + "Below is an example of 2 agents collaborating to accomplish a single task." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "075c91c3-c249-471d-b259-41975faa83fb", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "from typing import List, Tuple, Union\n", + "\n", + "import matplotlib.pyplot as plt\n", + "from langchain_core.tools import tool\n", + "\n", + "USER_DATA = [\n", + " {\n", + " \"id\": 1,\n", + " \"name\": \"Alice\",\n", + " \"email\": \"alice@gmail.com\",\n", + " \"favorite_color\": \"red\",\n", + " \"favorite_foods\": [1, 2, 3],\n", + " },\n", + " {\n", + " \"id\": 21,\n", + " \"name\": \"Bob\",\n", + " \"email\": \"bob@hotmail.com\",\n", + " \"favorite_color\": \"orange\",\n", + " \"favorite_foods\": [4, 5, 6],\n", + " },\n", + " {\n", + " \"id\": 35,\n", + " \"name\": \"Charlie\",\n", + " \"email\": \"charlie@yahoo.com\",\n", + " \"favorite_color\": \"yellow\",\n", + " \"favorite_foods\": [3, 7, 2],\n", + " },\n", + " {\n", + " \"id\": 41,\n", + " \"name\": \"Donna\",\n", + " \"email\": \"donna@example.com\",\n", + " \"favorite_color\": \"green\",\n", + " \"favorite_foods\": [6, 1, 4],\n", + " },\n", + " {\n", + " \"id\": 42,\n", + " \"name\": \"Eve\",\n", + " \"email\": \"eve@example.org\",\n", + " \"favorite_color\": \"blue\",\n", + " \"favorite_foods\": [5, 7, 4],\n", + " },\n", + " {\n", + " \"id\": 43,\n", + " \"name\": \"Frank The Cat\",\n", + " \"email\": \"frank.the.cat@langchain.dev\",\n", + " \"favorite_color\": \"yellow\",\n", + " \"favorite_foods\": [3],\n", + " },\n", + "]\n", + "\n", + "FOOD_DATA = [\n", + " {\n", + " \"id\": 1,\n", + " \"name\": \"Pizza\",\n", + " \"calories\": 285, # Calories per serving\n", + " \"allergic_ingredients\": [\"Gluten\", \"Dairy\"],\n", + " },\n", + " {\n", + " \"id\": 2,\n", + " \"name\": \"Chocolate\",\n", + " \"calories\": 50, # Calories per serving\n", + " \"allergic_ingredients\": [\"Milk\", \"Soy\"],\n", + " },\n", + " {\n", + " \"id\": 3,\n", + " \"name\": \"Sushi\",\n", + " \"calories\": 300, # Calories per serving\n", + " \"allergic_ingredients\": [\"Fish\", \"Soy\"],\n", + " },\n", + " {\n", + " \"id\": 4,\n", + " \"name\": \"Burger\",\n", + " \"calories\": 350, # Calories per serving\n", + " \"allergic_ingredients\": [\"Gluten\", \"Dairy\"],\n", + " },\n", + " {\n", + " \"id\": 5,\n", + " \"name\": \"Ice Cream\",\n", + " \"calories\": 200, # Calories per serving\n", + " \"allergic_ingredients\": [\"Dairy\"],\n", + " },\n", + " {\n", + " \"id\": 6,\n", + " \"name\": \"Pasta\",\n", + " \"calories\": 180, # Calories per serving\n", + " \"allergic_ingredients\": [\"Gluten\"],\n", + " },\n", + " {\n", + " \"id\": 7,\n", + " \"name\": \"Salad\",\n", + " \"calories\": 50, # Calories per serving\n", + " \"allergic_ingredients\": [],\n", + " },\n", + "]\n", + "\n", + "\n", + "@tool\n", + "def list_users() -> List[str]:\n", + " \"\"\"Return a list of all the user names.\"\"\"\n", + " return [user_info[\"name\"] for user_info in USER_DATA]\n", + "\n", + "\n", + "@tool\n", + "def get_user_favorite_food_ids(user_id: int) -> List[int]:\n", + " \"\"\"Get the favorite foods of the user with the provided ID.\"\"\"\n", + " for user_info in USER_DATA:\n", + " if user_info[\"id\"] == user_id:\n", + " return user_info[\"favorite_foods\"]\n", + " raise ValueError(f\"Id: {user_id} not found.\")\n", + "\n", + "\n", + "@tool\n", + "def get_food_name(food_id: int) -> str:\n", + " \"\"\"Get the food name.\"\"\"\n", + " for food_info in FOOD_DATA:\n", + " if food_info[\"id\"] == food_id:\n", + " return food_info[\"name\"]\n", + " raise ValueError(f\"Id: {food_id} not found.\")\n", + "\n", + "\n", + "@tool\n", + "def get_user_id(user_name: str) -> int:\n", + " \"\"\"Return the ID of the provided user name.\"\"\"\n", + " for user_info in USER_DATA:\n", + " if user_info[\"name\"] == user_name:\n", + " return user_info[\"id\"]\n", + " raise ValueError(f\"Name: {user_name} not found.\")\n", + "\n", + "\n", + "@tool\n", + "def create_barplot(\n", + " data: Union[List[float], List[int]],\n", + " labels: Union[List[str], None] = None,\n", + " title: str = \"Bar Plot\",\n", + " xlabel: str = \"X\",\n", + " ylabel: str = \"Y\",\n", + " color: Union[str, List[str]] = \"blue\",\n", + ") -> Tuple[plt.Figure, plt.Axes]:\n", + " \"\"\"\n", + " Generates a bar plot from the provided data and returns the figure and axis objects.\n", + "\n", + " :param data: A list of numerical values for the bar heights.\n", + " :param labels: A list of strings for the bar labels. Default is None.\n", + " :param title: Title of the plot. Default is 'Bar Plot'.\n", + " :param xlabel: Label for the X-axis. Default is 'X'.\n", + " :param ylabel: Label for the Y-axis. Default is 'Y'.\n", + " :param color: Color of the bars. Can be a single color or a list of colors. Default is 'blue'.\n", + " :param figsize: Size of the figure as a tuple (width, height). Default is (10, 6).\n", + " :return: Tuple containing the figure and axes objects.\n", + " \"\"\"\n", + " fig, ax = plt.subplots(figsize=(10, 6))\n", + " x_positions = range(len(data))\n", + "\n", + " if labels and len(labels) == len(data):\n", + " plt.xticks(x_positions, labels)\n", + "\n", + " ax.bar(x_positions, data, color=color)\n", + " ax.set_title(title)\n", + " ax.set_xlabel(xlabel)\n", + " ax.set_ylabel(ylabel)\n", + " plt.savefig(\"./plot.png\")\n", + " return \"./plot.png\"" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b0ca7d80-31e4-4394-bfce-ffac314bac7d", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", + "from langchain.agents import create_openai_functions_agent\n", + "from langchain.tools.render import format_tool_to_openai_function\n", + "from langchain_core.messages import (\n", + " AIMessage,\n", + " BaseMessage,\n", + " FunctionMessage,\n", + " HumanMessage,\n", + ")\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_openai import ChatOpenAI\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import END, StateGraph\n", + "from langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation\n", + "\n", + "\n", + "class AgentState(TypedDict):\n", + " messages: Annotated[Sequence[BaseMessage], operator.add]\n", + " sender: str" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "4dce3901-6ad5-4df5-8528-6e865cf96cb0", + "metadata": {}, + "outputs": [], + "source": [ + "workflow = StateGraph(AgentState)\n", + "\n", + "# This a helper class we have that is useful for running tools\n", + "# It takes in an agent action and calls that tool and returns the result\n", + "tools = [\n", + " list_users,\n", + " get_user_favorite_food_ids,\n", + " get_food_name,\n", + " get_user_id,\n", + " create_barplot,\n", + "]\n", + "tool_executor = ToolExecutor(tools)\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are a helpful AI assistant, collaborating with other assistants.\"\n", + " \" Use the provided tools to progress towards answering the question.\"\n", + " \" If you are unable to fully answer, that's OK, another assistant with different tools \"\n", + " \" will help where you left off. Execute what you can to \"\n", + " \" If you have the final answer, prefix your response with FINAL ANSWER:\",\n", + " ),\n", + " MessagesPlaceholder(variable_name=\"messages\"),\n", + " ]\n", + ")\n", + "\n", + "\n", + "def add_agent_node(name: str, llm, tools, prompt):\n", + " functions = [format_tool_to_openai_function(t) for t in tools]\n", + " chain = (\n", + " (lambda x: {**x, \"intermediate_steps\": []})\n", + " | prompt\n", + " | llm.bind_functions(functions)\n", + " | (lambda x: {\"messages\": [x], \"sender\": name})\n", + " )\n", + " workflow.add_node(name, chain)\n", + "\n", + "\n", + "def call_tool(state):\n", + " messages = state[\"messages\"]\n", + " # Based on the continue condition\n", + " # we know the last message involves a function call\n", + " last_message = messages[-1]\n", + " # We construct an ToolInvocation from the function_call\n", + " action = ToolInvocation(\n", + " tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n", + " tool_input=json.loads(\n", + " last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n", + " ),\n", + " )\n", + " # We call the tool_executor and get back a response\n", + " response = tool_executor.invoke(action)\n", + " # We use the response to create a FunctionMessage\n", + " function_message = FunctionMessage(content=str(response), name=action.tool)\n", + " # We return a list, because this will get added to the existing list\n", + " return {\"messages\": [function_message]}\n", + "\n", + "\n", + "def should_continue(state):\n", + " # This is the router\n", + " messages = state[\"messages\"]\n", + " last_message = messages[-1]\n", + " if \"function_call\" in last_message.additional_kwargs:\n", + " return \"call_tool\"\n", + " if \"FINAL ANSWER\" in last_message.content:\n", + " return \"end\"\n", + " return \"continue\"\n", + "\n", + "\n", + "llm = ChatOpenAI(model=\"gpt-4\")\n", + "add_agent_node(\n", + " \"user_info\",\n", + " llm,\n", + " [\n", + " list_users,\n", + " get_user_favorite_food_ids,\n", + " get_food_name,\n", + " get_user_id,\n", + " ],\n", + " prompt,\n", + ")\n", + "add_agent_node(\"plot_bars\", llm, [create_barplot], prompt)\n", + "workflow.add_node(\"call_tool\", call_tool)\n", + "workflow.add_conditional_edges(\n", + " \"user_info\",\n", + " should_continue,\n", + " {\"continue\": \"plot_bars\", \"call_tool\": \"call_tool\", \"end\": END},\n", + ")\n", + "workflow.add_conditional_edges(\n", + " \"plot_bars\",\n", + " should_continue,\n", + " {\"continue\": \"user_info\", \"call_tool\": \"call_tool\", \"end\": END},\n", + ")\n", + "# We will let the user_info tool decide when to respond.\n", + "workflow.add_edge(\"call_tool\", \"user_info\")\n", + "workflow.set_entry_point(\"user_info\")\n", + "graph = workflow.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "176a99b0-b457-45cf-8901-90facaa852da", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='Draw a bar plot of the favorite foods of Eve and Donna'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"user_name\": \"Eve\"\\n}', 'name': 'get_user_id'}}),\n", + " FunctionMessage(content='42', name='get_user_id'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"user_id\": 42\\n}', 'name': 'get_user_favorite_food_ids'}}),\n", + " FunctionMessage(content='[5, 7, 4]', name='get_user_favorite_food_ids'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 5\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Ice Cream', name='get_food_name'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 7\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Salad', name='get_food_name'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 4\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Burger', name='get_food_name'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"user_name\": \"Donna\"\\n}', 'name': 'get_user_id'}}),\n", + " FunctionMessage(content='41', name='get_user_id'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"user_id\": 41\\n}', 'name': 'get_user_favorite_food_ids'}}),\n", + " FunctionMessage(content='[6, 1, 4]', name='get_user_favorite_food_ids'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 6\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Pasta', name='get_food_name'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 1\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Pizza', name='get_food_name'),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"food_id\": 4\\n}', 'name': 'get_food_name'}}),\n", + " FunctionMessage(content='Burger', name='get_food_name'),\n", + " AIMessage(content=\"Based on the data obtained, we can make the following observations about the favorite foods of Eve and Donna:\\n\\n- Eve's favorite foods are Ice Cream, Salad, and Burger.\\n- Donna's favorite foods are Pasta, Pizza, and Burger.\\n\\nTo draw a bar plot, we need a tool with graphical capabilities which is currently not available in this environment. However, if we were to represent this data on a bar plot, we would have 'Food Items' on the x-axis and 'Count' on the y-axis. Each user would have a different color bar. For each food item, there would be a bar representing the count of users who have that food as their favorite. \\n\\nFor example, 'Burger' would have a count of 2 because both Eve and Donna have it as their favorite food. Other food items like 'Ice Cream', 'Salad', 'Pasta', and 'Pizza' would have a count of 1 as they are the favorite of only one user. \\n\\nPlease use a tool with graphical capabilities like Python's matplotlib library or Excel to create this bar plot.\"),\n", + " AIMessage(content=\"The current environment doesn't support graphical outputs. However, I can provide you with the data and the steps to create the bar plot on your local machine using Python's matplotlib library. \\n\\nHere is the data:\\n\\n```python\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n```\\n\\nYou can use the following code to create the bar plot:\\n\\n```python\\nimport matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n\\nx = np.arange(len(food_items)) # the label locations\\nwidth = 0.35 # the width of the bars\\n\\nfig, ax = plt.subplots()\\nrects1 = ax.bar(x - width/2, eve_favorites, width, label='Eve')\\nrects2 = ax.bar(x + width/2, donna_favorites, width, label='Donna')\\n\\n# Add some text for labels, title and custom x-axis tick labels, etc.\\nax.set_ylabel('Count')\\nax.set_title('Favorite foods of Eve and Donna')\\nax.set_xticks(x)\\nax.set_xticklabels(food_items)\\nax.legend()\\n\\nfig.tight_layout()\\n\\nplt.show()\\n```\\n\\nThis will create a bar plot with food items on the x-axis and the count on the y-axis. The two sets of bars represent Eve's and Donna's favorite food respectively.\"),\n", + " AIMessage(content=\"The current environment doesn't support graphical outputs. However, I can provide you with the data and the steps to create the bar plot on your local machine using Python's matplotlib library. \\n\\nHere is the data:\\n\\n```python\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n```\\n\\nYou can use the following code to create the bar plot:\\n\\n```python\\nimport matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n\\nx = np.arange(len(food_items)) # the label locations\\nwidth = 0.35 # the width of the bars\\n\\nfig, ax = plt.subplots()\\nrects1 = ax.bar(x - width/2, eve_favorites, width, label='Eve')\\nrects2 = ax.bar(x + width/2, donna_favorites, width, label='Donna')\\n\\n# Add some text for labels, title and custom x-axis tick labels, etc.\\nax.set_ylabel('Count')\\nax.set_title('Favorite foods of Eve and Donna')\\nax.set_xticks(x)\\nax.set_xticklabels(food_items)\\nax.legend()\\n\\nfig.tight_layout()\\n\\nplt.show()\\n```\\n\\nThis will create a bar plot with food items on the x-axis and the count on the y-axis. The two sets of bars represent Eve's and Donna's favorite food respectively.\"),\n", + " AIMessage(content=\"The current environment doesn't support graphical outputs. However, I can provide you with the data and the steps to create the bar plot on your local machine using Python's matplotlib library. \\n\\nHere is the data:\\n\\n```python\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n```\\n\\nYou can use the following code to create the bar plot:\\n\\n```python\\nimport matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n\\nx = np.arange(len(food_items)) # the label locations\\nwidth = 0.35 # the width of the bars\\n\\nfig, ax = plt.subplots()\\nrects1 = ax.bar(x - width/2, eve_favorites, width, label='Eve')\\nrects2 = ax.bar(x + width/2, donna_favorites, width, label='Donna')\\n\\n# Add some text for labels, title and custom x-axis tick labels, etc.\\nax.set_ylabel('Count')\\nax.set_title('Favorite foods of Eve and Donna')\\nax.set_xticks(x)\\nax.set_xticklabels(food_items)\\nax.legend()\\n\\nfig.tight_layout()\\n\\nplt.show()\\n```\\n\\nThis will create a bar plot with food items on the x-axis and the count on the y-axis. The two sets of bars represent Eve's and Donna's favorite food respectively.\"),\n", + " AIMessage(content=\"The current environment doesn't support graphical outputs. However, I can provide you with the data and the steps to create the bar plot on your local machine using Python's matplotlib library. \\n\\nHere is the data:\\n\\n```python\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n```\\n\\nYou can use the following code to create the bar plot:\\n\\n```python\\nimport matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n\\nx = np.arange(len(food_items)) # the label locations\\nwidth = 0.35 # the width of the bars\\n\\nfig, ax = plt.subplots()\\nrects1 = ax.bar(x - width/2, eve_favorites, width, label='Eve')\\nrects2 = ax.bar(x + width/2, donna_favorites, width, label='Donna')\\n\\n# Add some text for labels, title and custom x-axis tick labels, etc.\\nax.set_ylabel('Count')\\nax.set_title('Favorite foods of Eve and Donna')\\nax.set_xticks(x)\\nax.set_xticklabels(food_items)\\nax.legend()\\n\\nfig.tight_layout()\\n\\nplt.show()\\n```\\n\\nThis will create a bar plot with food items on the x-axis and the count on the y-axis. The two sets of bars represent Eve's and Donna's favorite food respectively.\"),\n", + " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"data\": [1, 1, 2, 1, 1],\\n \"labels\": [\"Ice Cream\", \"Salad\", \"Burger\", \"Pasta\", \"Pizza\"],\\n \"title\": \"Favorite Foods of Eve and Donna\",\\n \"xlabel\": \"Food Items\",\\n \"ylabel\": \"Count\",\\n \"color\": [\"blue\", \"blue\", \"blue\", \"orange\", \"orange\"]\\n}', 'name': 'create_barplot'}}),\n", + " FunctionMessage(content='./plot.png', name='create_barplot'),\n", + " AIMessage(content=\"I'm sorry, I made a mistake. This environment doesn't support the creation or display of plots. You can use the data and instructions provided earlier to create the plot in a local Python environment or any other tool that supports bar plots.\"),\n", + " AIMessage(content=\"I'm sorry, I made a mistake. This environment doesn't support the creation or display of plots. You can use the data and instructions provided earlier to create the plot in a local Python environment or any other tool that supports bar plots.\"),\n", + " AIMessage(content=\"I'm sorry, I made a mistake. This environment doesn't support the creation or display of plots. You can use the data and instructions provided earlier to create the plot in a local Python environment or any other tool that supports bar plots.\"),\n", + " AIMessage(content=\"I'm sorry, I made a mistake. This environment doesn't support the creation or display of plots. You can use the data and instructions provided earlier to create the plot in a local Python environment or any other tool that supports bar plots.\"),\n", + " AIMessage(content=\"FINAL ANSWER:\\n\\nUnfortunately, I'm unable to draw a bar plot in this environment. However, I have provided the data and the Python code needed to create the bar plot in your local environment using the matplotlib library.\\n\\nHere is the data:\\n\\n```python\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n```\\n\\nYou can use the following Python code to create the bar plot:\\n\\n```python\\nimport matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfood_items = ['Ice Cream', 'Salad', 'Burger', 'Pasta', 'Pizza']\\neve_favorites = [1, 1, 1, 0, 0]\\ndonna_favorites = [0, 0, 1, 1, 1]\\n\\nx = np.arange(len(food_items)) # the label locations\\nwidth = 0.35 # the width of the bars\\n\\nfig, ax = plt.subplots()\\nrects1 = ax.bar(x - width/2, eve_favorites, width, label='Eve')\\nrects2 = ax.bar(x + width/2, donna_favorites, width, label='Donna')\\n\\n# Add some text for labels, title and custom x-axis tick labels, etc.\\nax.set_ylabel('Count')\\nax.set_title('Favorite foods of Eve and Donna')\\nax.set_xticks(x)\\nax.set_xticklabels(food_items)\\nax.legend()\\n\\nfig.tight_layout()\\n\\nplt.show()\\n```\")],\n", + " 'sender': 'user_info'}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1cAAAIjCAYAAADvBuGTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/OQEPoAAAACXBIWXMAAA9hAAAPYQGoP6dpAABSDUlEQVR4nO3deVhV1eL/8c8B4aAoODMUgYo5pZJjmqYWiqZerW6pDaJXrSwzozLplnNZlmaDSlqOZXlNs66VQ5iahpoDdS3zqheHFJwSEExQWL8/+rG/ngAF23pA36/n2U+dtddee+3DYns+7L3XcRhjjAAAAAAAf4mHuzsAAAAAAFcDwhUAAAAA2IBwBQAAAAA2IFwBAAAAgA0IVwAAAABgA8IVAAAAANiAcAUAAAAANiBcAQAAAIANCFcAAAAAYAPCFQCUQnPmzJHD4dC+ffvc3ZUrqn379mrfvv0V3WdGRoYGDhyowMBAORwODRs27IruvzTo16+fwsLC3N0NAHA7whWAa15eUCloGTFihLu7V2TTpk3TnDlzbG+3ffv2hb4/v/zyi+37K2lefvllzZkzR4MHD9b8+fP10EMPFVo3LCys0Peqc+fOV7DXJdP5Y8nDw0N+fn6qU6eOHnroIa1atcrd3QOAv6yMuzsAACXF2LFjVaNGDZeym266yU29ubCHHnpIvXv3ltPptMqmTZumqlWrql+/frbv7/rrr9eECRPylQcHB9u+r5Jm9erVuuWWWzRq1Kgi1Y+IiNDTTz+dr/xaeK+K4vyxlJmZqT179mjJkiX64IMPdN999+mDDz6Ql5eXm3sJAJeGcAUA/1+XLl3UrFkzd3fjgjIzM+Xr6ytPT095enpesf36+/vrwQcfvGL7K0mOHj2q+vXrF7n+ddddd82+V0VR0Fh65ZVXNHToUE2bNk1hYWF69dVX3dQ7APhruC0QAC5i//79euyxx1SnTh2VLVtWVapU0b333uvyvNOWLVvkcDg0d+7cfNuvWLFCDodDy5Yts8q2b9+uLl26yM/PT+XLl9cdd9yhjRs3umyXd7vi2rVr9dhjj6l69eq6/vrrXdbl9SEsLEw//fST1q5da912df6zSampqRo2bJhCQkLkdDoVHh6uV199Vbm5uba8R+fOndO4ceNUq1YtOZ1OhYWF6fnnn1dWVla+utOmTVODBg3kdDoVHBysxx9/XKmpqfnqzZgxQ7Vq1VLZsmXVokULffvttwXu++2331aDBg1Urlw5VapUSc2aNdOCBQsu2uejR49qwIABCggIkI+Pjxo3buzy81uzZo0cDoeSkpL0xRdfWO/rX33O7fXXX5fD4dD+/fvzrYuNjZW3t7dOnjxplW3atEmdO3eWv7+/ypUrp3bt2mnDhg0X3U92drZGjhyppk2byt/fX76+vmrbtq2++eYbl3r79u2Tw+HQ66+/br3nTqdTzZs31/fff5+v3aVLl+qmm26Sj4+PbrrpJn366aeX8C648vT01FtvvaX69evrnXfeUVpamrWuqGMrLCxM3bp10/r169WiRQv5+PioZs2amjdvnku9vN+dDRs2KCYmRtWqVZOvr6/uuusuHTt2zKXuZ599pq5duyo4OFhOp1O1atXSuHHjlJOT85ePGcBVygDANW727NlGkvn666/NsWPHXBZjjFm0aJFp3LixGTlypJkxY4Z5/vnnTaVKlUxoaKjJzMy02qlZs6a5884787Xfv39/U6lSJZOdnW2MMWbHjh3G19fXBAUFmXHjxplXXnnF1KhRwzidTrNx48Z8/apfv75p166defvtt80rr7zisi4pKckYY8ynn35qrr/+elO3bl0zf/58M3/+fLNy5UpjjDGZmZmmUaNGpkqVKub55583cXFxpm/fvsbhcJgnn3zyou9Pu3btTN26dfO9N6dOnbLqREdHG0nm73//u5k6darp27evkWR69uzp0taoUaOMJBMZGWnefvttM2TIEOPp6WmaN29uvT/GGPPee+8ZSaZ169bmrbfeMsOGDTMVK1Y0NWvWNO3atbPqzZgxw9rvu+++a958800zYMAAM3To0Ase0+nTp029evWMl5eXeeqpp8xbb71l2rZtaySZKVOmGGOMSUlJMfPnzzdVq1Y1ERER1vuakZFRaLuhoaGmU6dO+d6rY8eOmdOnTxtjjNm/f79xOBxm4sSJ+bavWbOm6dq1q/U6Pj7eeHt7m1atWplJkyaZN954wzRq1Mh4e3ubTZs2XfAYjx07ZoKCgkxMTIyZPn26mThxoqlTp47x8vIy27dvt+olJSUZSebmm2824eHh5tVXXzUTJ040VatWNddff73Lz2XFihXGw8PD3HTTTWby5Mnmn//8p/H39zcNGjQwoaGhF+yPMX+MpQYNGhS6fty4cUaSWbZsmVVW1LEVGhpq6tSpYwICAszzzz9v3nnnHdOkSRPjcDjMjh07rHp5vzs333yzuf32283bb79tnn76aePp6Wnuu+8+lzZ79uxp7rvvPvPaa6+Z6dOnm3vvvddIMs8888xFjxXAtYlwBeCal/dhq6DFGGN9KD5fQkKCkWTmzZtnlcXGxhovLy/z22+/WWVZWVmmYsWK5h//+IdV1rNnT+Pt7W327t1rlR0+fNhUqFDB3Hbbbfn61aZNG3Pu3LkC+5wXrowxpkGDBi7BI8+4ceOMr6+v+e9//+tSPmLECOPp6WkOHDhwwfenXbt2Bb430dHRxhhjEhMTjSQzcOBAl+2eeeYZI8msXr3aGGPM0aNHjbe3t+nUqZPJycmx6r3zzjtGkpk1a5Yxxpjs7GxTvXp1ExERYbKysqx6eUHq/GPs0aPHBT+sF2bKlClGkvnggw+ssuzsbNOqVStTvnx5k56ebpWHhoa6BJ4LCQ0NLXQsTZgwwarXqlUr07RpU5dtN2/e7DKmcnNzTe3atU1UVJTJzc216p0+fdrUqFHDdOzY8YJ9OXfunMv7Z4wxJ0+eNAEBAS7jMS9cValSxWXsfvbZZ0aS+fe//22VRUREmKCgIJOammqVrVy50kiyJVx9+umnRpJ58803jTFFH1vG/N97v27dOqvs6NGjxul0mqefftoqy/vdiYyMdHlfn3rqKePp6elybAX97j/yyCOmXLly5syZMxc9XgDXHm4LBID/b+rUqVq1apXLIklly5a16pw9e1YnTpxQeHi4KlasqG3btlnrevXqpbNnz2rJkiVW2cqVK5WamqpevXpJknJycrRy5Ur17NlTNWvWtOoFBQXp/vvv1/r165Wenu7Sr0GDBv2l56sWLVqktm3bqlKlSjp+/Li1REZGKicnR+vWrbtoG2FhYfnem+HDh0uSvvzyS0lSTEyMyzZ5kzp88cUXkqSvv/5a2dnZGjZsmDw8/u+fn0GDBsnPz8+qt2XLFh09elSPPvqovL29rXr9+vWTv7+/yz4qVqyoX3/9tcDb1y7kyy+/VGBgoPr06WOVeXl5aejQocrIyNDatWuL1d75WrZsme+9WrVqlcu+evXqpa1bt2rv3r1W2cKFC+V0OtWjRw9JUmJionbv3q37779fJ06csH5umZmZuuOOO7Ru3boL3tbp6elpvX+5ubn67bffdO7cOTVr1sxl3J7fp0qVKlmv27ZtK0n63//+J0lKTk5WYmKioqOjXX4OHTt2LNYzaRdSvnx5SdKpU6ckFX1s5alfv77Vb0mqVq2a6tSpYx3D+R5++GE5HA7rddu2bZWTk+Nyu+b5v/unTp3S8ePH1bZtW50+ffqamCkTQPExoQUA/H8tWrQocEKL33//XRMmTNDs2bN16NAhGWOsdec/G9K4cWPVrVtXCxcu1IABAyT98YG5atWquv322yVJx44d0+nTp1WnTp18+6lXr55yc3N18OBBNWjQwCr/8wyGxbV79279+OOPqlatWoHrjx49etE2fH19FRkZWeC6/fv3y8PDQ+Hh4S7lgYGBqlixovVhNe+/fz52b29v1axZM1+92rVru9Tz8vJyCaSS9Nxzz+nrr79WixYtFB4erk6dOun+++/XrbfeesHj2b9/v2rXru0S8qQ/fgbn9+FSVK1atdD3Ks+9996rmJgYLVy4UM8//7yMMVq0aJH1HJ70x89NkqKjowttJy0tzSUQ/dncuXM1adIk/fLLLzp79qxVXtCYuuGGG1xe57Wb9/xXYT8X6Y+faUGBrbgyMjIkSRUqVLD2WZSxVdgxSH8cx/nPsBVW98/HK0k//fSTXnjhBa1evTrfHz3O/90HgDyEKwC4iCeeeEKzZ8/WsGHD1KpVK/n7+8vhcKh37975rhz06tVLL730ko4fP64KFSro888/V58+fVSmzKWfbs//6/mlyM3NVceOHa0rTX924403/qX285x/FeBKqVevnnbt2qVly5Zp+fLlWrx4saZNm6aRI0dqzJgxV7w/RRUcHKy2bdvqX//6l55//nlt3LhRBw4ccJklL29svfbaa4qIiCiwnbwrPQX54IMP1K9fP/Xs2VPPPvusqlevLk9PT02YMMHlilmewq6Onv/HhMttx44dkpQvTBV1bBXnGC5WNzU1Ve3atZOfn5/Gjh2rWrVqycfHR9u2bdNzzz1n22QwAK4uhCsAuIhPPvlE0dHRmjRpklV25syZAme469Wrl8aMGaPFixcrICBA6enp6t27t7W+WrVqKleunHbt2pVv219++UUeHh4KCQm5pH4W9gG0Vq1aysjIuOjVlEsVGhqq3Nxc7d6927ryI0lHjhxRamqqQkNDrXqStGvXLpcrUNnZ2UpKSrL6l1dv9+7d1hU/6Y9bMpOSktS4cWOX/fv6+qpXr17q1auXsrOzdffdd+ull15SbGysfHx8Cu3zjz/+qNzcXJerV3m3euX14XLq1auXHnvsMe3atUsLFy5UuXLl1L17d2t9rVq1JEl+fn6X9LP75JNPVLNmTS1ZssRlbBT1+7r+7Pyfy58VNJ6LKycnRwsWLFC5cuXUpk0ba59FGVuXw5o1a3TixAktWbJEt912m1WelJR02fYJoPTjmSsAuAhPT898f/l+++23C5yOuV69emrYsKEWLlyohQsXKigoyOWDmaenpzp16qTPPvvMZUrvI0eOaMGCBWrTpo11W1hx+fr6Fhj47rvvPiUkJGjFihX51qWmpurcuXOXtL88d955pyRpypQpLuWTJ0+WJHXt2lWSFBkZKW9vb7311lsu7+f777+vtLQ0q16zZs1UrVo1xcXFKTs726o3Z86cfMd34sQJl9fe3t6qX7++jDEut8EV1OeUlBQtXLjQKjt37pzefvttlS9fXu3atSvi0V+6e+65R56envroo4+0aNEidevWTb6+vtb6pk2bqlatWnr99det2+XO9+dpw/8s78rM+e/1pk2blJCQcEn9DQoKUkREhObOnetyS9yqVav0888/X1KbeXJycjR06FDt3LlTQ4cOtX4Hijq2LoeC3r/s7GxNmzbtsu0TQOnHlSsAuIhu3bpp/vz58vf3V/369ZWQkKCvv/5aVapUKbB+r169NHLkSPn4+GjAgAH5nusZP368Vq1apTZt2uixxx5TmTJl9O677yorK0sTJ0685H42bdpU06dP1/jx4xUeHq7q1avr9ttv17PPPqvPP/9c3bp1U79+/dS0aVNlZmbqP//5jz755BPt27dPVatWveT9Nm7cWNHR0ZoxY4Z1K9XmzZs1d+5c9ezZUx06dJD0x1W72NhYjRkzRp07d9bf/vY37dq1S9OmTVPz5s2tL5b18vLS+PHj9cgjj+j2229Xr169lJSUpNmzZ+d75qpTp04KDAzUrbfeqoCAAO3cuVPvvPOOunbtaj23U5CHH35Y7777rvr166etW7cqLCxMn3zyiTZs2KApU6ZccNuLOXTokD744IN85eXLl1fPnj2t19WrV1eHDh00efJknTp1ypr0JI+Hh4fee+89denSRQ0aNFD//v113XXX6dChQ/rmm2/k5+enf//734X2o1u3blqyZInuuusude3aVUlJSYqLi1P9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TZqkH374QfPnzy9wm+zs7CvcS7jbPffco+3bt2vu3Ln673//q88//1zt27fXiRMn3N01lHCdO3dWcnKykpOTFR8frzJlyqhbt26X3F5OTo5yc3Nt7OH/4dxW+uSNr927d+vpp5/W6NGj9dprr7m7WyilChtP/v7+ttx9AfchXKFAubm5mjhxosLDw+V0OnXDDTfopZdestYfPHhQ9913nypWrKjKlSurR48e2rdvX6Htbd68WS+//LImTZqk1157Ta1bt1ZYWJg6duyoxYsXKzo6WpI0evRoRURE6L333lONGjWs7yZLTU3VwIEDVa1aNfn5+en222/XDz/8YLW/d+9e9ejRQwEBASpfvryaN2+ur7/+2qUPYWFhGj9+vPr27avy5csrNDRUn3/+uY4dO6YePXpYV9K2bNli4zuJ4khNTdW3336rV199VR06dFBoaKhatGih2NhY/e1vf5MkTZ48WQ0bNpSvr69CQkL02GOPKSMjo9A2izI2jh49qu7du6ts2bKqUaOGPvzww8t6nLg88q50BgYGKiIiQiNGjNDBgwd17NixAq8QJCYmyuFwWOeuvFtKP//8c9WvX19Op1MHDhxQcnKyunbtao2PBQsW5Ltt9GLnqMLObSg98sZXaGioBg8erMjISH3++ecXPSft379f3bt3V6VKleTr66sGDRroyy+/1L59+9ShQwdJUqVKleRwONSvXz9J0vLly9WmTRtVrFhRVapUUbdu3bR37153HDYuk8LG0/l3Eu3bt8/linze0r59e0lS+/btC1yfd04r7r+XsAfhCgWKjY3VK6+8ohdffFE///yzFixYYH2x89mzZxUVFaUKFSro22+/1YYNG1S+fHl17ty50L/Gfvjhhypfvrwee+yxAtef/1eaPXv2aPHixVqyZIkSExMlSffee6+OHj2qr776Slu3blWTJk10xx13WLcTZmRk6M4771R8fLy2b9+uzp07q3v37jpw4IDLft544w3deuut2r59u7p27aqHHnpIffv21YMPPqht27apVq1a6tu3r/j6N/coX768ypcvr6VLlyorK6vAOh4eHnrrrbf0008/ae7cuVq9erWGDx9eaJtFGRv9+vXTwYMH9c033+iTTz7RtGnTdPToUduPD1dORkaGPvjgA4WHh6tKlSpF3u706dN69dVX9d577+mnn35S9erV1bdvXx0+fFhr1qzR4sWLNWPGjHzj42LnKKngcxtKr7Jlyyo7O/ui56THH39cWVlZWrdunf7zn//o1VdfVfny5RUSEqLFixdLknbt2qXk5GS9+eabkqTMzEzFxMRoy5Ytio+Pl4eHh+66667LdiUV7pc3ns4XEhJiXY1PTk7W9u3bVaVKFd12222SpCVLlrisv/vuu1WnTh3r81px/72ETQxgjImOjjY9evQwxhiTnp5unE6nmTlzZoF158+fb+rUqWNyc3OtsqysLFO2bFmzYsWKArfp0qWLadSo0UX7MWrUKOPl5WWOHj1qlX377bfGz8/PnDlzxqVurVq1zLvvvltoWw0aNDBvv/229To0NNQ8+OCD1uvk5GQjybz44otWWUJCgpFkkpOTL9pXXB6ffPKJqVSpkvHx8TGtW7c2sbGx5ocffii0/qJFi0yVKlWs17Nnzzb+/v4X3Mf5Y2PXrl1Gktm8ebO1fufOnUaSeeONN/7SseDKiY6ONp6ensbX19f4+voaSSYoKMhs3brVGGPMN998YySZkydPWtts377dSDJJSUnGmD/GjiSTmJho1ckbC99//71Vtnv3bpfxUZRzVEHnNpQe5/8bmZuba1atWmWcTqd55pln8tX98zmpYcOGZvTo0QW2W9C4LMixY8eMJPOf//znko8BJceFxtP56873+++/m5YtW5pu3bqZnJycfOsnT55sKlasaHbt2lXofv88NnF5cOUK+ezcuVNZWVm64447Clz/ww8/aM+ePapQoYJ1paFy5co6c+ZMobctmGJcCQoNDVW1atVc9peRkaEqVapY+ytfvrySkpKs/WVkZOiZZ55RvXr1VLFiRZUvX147d+7Md+WqUaNG1v/n/WWnYcOG+cq4auE+99xzjw4fPqzPP/9cnTt31po1a9SkSRPrAd+vv/5ad9xxh6677jpVqFBBDz30kE6cOKHTp08X2N7FxsbOnTtVpkwZNW3a1Nqmbt263PNeCnXo0EGJiYlKTEzU5s2bFRUVpS5dumj//v1FbsPb29vlPLFr1y6VKVNGTZo0scrCw8NVqVIl63VRzlFS/nMbSpdly5apfPny8vHxUZcuXdSrVy+NHj36ouekoUOHavz48br11ls1atSoIk2ysnv3bvXp00c1a9aUn5+fwsLCJCnfv2kovQobT4X5xz/+oVOnTmnBggXy8HD9+P7V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UOI99thjOnjwoJ544gn98ssv+uyzzzRq1CjFxMTIw8NDvr6+Gjx4sJ599lktX75cP//8swYNGqTTp09rwIABkqRHH31Uu3fv1rPPPqtdu3ZpwYIFmjNnTrH7EhYWpvj4eKWkpOjkyZOSpJEjR2revHkaM2aMfvrpJ+3cuVMff/yxXnjhhUs+5t9//11DhgzRmjVrtH//fm3YsEHff/+96tWrd8ltAgAuL8IVAKDEu+666/Tll19q8+bNaty4sR599FENGDDAJby88soruueee/TQQw+pSZMm2rNnj1asWKFKlSpJkm644QYtXrxYS5cuVePGjRUXF6eXX3652H2ZNGmSVq1apZCQEN18882SpKioKC1btkwrV65U8+bNdcstt+iNN95QaGjoJR+zp6enTpw4ob59++rGG2/Ufffdpy5dumjMmDGX3CYA4PJymPPvsQAAAAAAXBKuXAEAAACADQhXAAAAAGADwhUAAAAA2IBwBQAAAAA2IFwBAAAAgA0IVwAAAABgA8IVAAAAANiAcAUAAAAANiBcAQAAAIANCFcAAAAAYAPCFQAAAADY4P8Bw6j3d18E1YkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = graph.invoke(\n", + " {\n", + " \"messages\": [\n", + " HumanMessage(\n", + " content=\"Draw a bar plot of the favorite foods of Eve and Donna\"\n", + " )\n", + " ]\n", + " },\n", + " # Maximum number of steps to take in the graph\n", + " {\"recursion_limit\": 150},\n", + ")\n", + "result[\"messages\"][-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e88f835-2c94-4fe1-90d6-c9edac105195", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/advanced_agents/multi-agent/plot.png b/examples/advanced_agents/multi-agent/plot.png new file mode 100644 index 0000000000000000000000000000000000000000..33adddc8c350a9a41788234e3886ac4a49953e2b GIT binary patch literal 21953 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 literal 0 HcmV?d00001