{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ab4ffc65-4ec2-41f5-b225-e8a7a4c3799f",
   "metadata": {},
   "source": [
    "# OpenAI assistants\n",
    "\n",
    "> The [Assistants API](https://platform.openai.com/docs/assistants/overview) allows you to build AI assistants within your own applications. An Assistant has instructions and can leverage models, tools, and knowledge to respond to user queries. The Assistants API currently supports three types of tools: Code Interpreter, Retrieval, and Function calling\n",
    "\n",
    "\n",
    "You can interact with OpenAI Assistants using OpenAI tools or custom tools. When using exclusively OpenAI tools, you can just invoke the assistant directly and get final answers. When using custom tools, you can run the assistant and tool execution loop using the built-in AgentExecutor or easily write your own executor.\n",
    "\n",
    "Below we show the different ways to interact with Assistants. As a simple example, let's build a math tutor that can write and run code."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2128b7b-01ff-477e-a41c-98026e0180ac",
   "metadata": {},
   "source": [
    "### Using only OpenAI tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "aa761a93-caa1-4e56-b901-5ff50a89bc82",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents.openai_assistant import OpenAIAssistantRunnable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "5944a18a-95eb-44ce-a66f-5f50db1d3e1f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ThreadMessage(id='msg_qgxkD5kvkZyl0qOaL4czPFkZ', assistant_id='asst_0T8S7CJuUa4Y4hm1PF6n62v7', content=[MessageContentText(text=Text(annotations=[], value='The result of the calculation \\\\(10 - 4^{2.7}\\\\) is approximately \\\\(-32.224\\\\).'), type='text')], created_at=1700169519, file_ids=[], metadata={}, object='thread.message', role='assistant', run_id='run_aH3ZgSWNk3vYIBQm3vpE8tr4', thread_id='thread_9K6cYfx1RBh0pOWD8SxwVWW9')]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "interpreter_assistant = OpenAIAssistantRunnable.create_assistant(\n",
    "    name=\"langchain assistant\",\n",
    "    instructions=\"You are a personal math tutor. Write and run code to answer math questions.\",\n",
    "    tools=[{\"type\": \"code_interpreter\"}],\n",
    "    model=\"gpt-4-1106-preview\",\n",
    ")\n",
    "output = interpreter_assistant.invoke({\"content\": \"What's 10 - 4 raised to the 2.7\"})\n",
    "output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2dbaba4e-3f2f-4b5d-bfc9-fc5ad4a05232",
   "metadata": {},
   "source": [
    "### As a LangChain agent with arbitrary tools\n",
    "\n",
    "Now let's recreate this functionality using our own tools. For this example we'll use the [E2B sandbox runtime tool](https://e2b.dev/docs?ref=landing-page-get-started)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4fb3305-525b-4739-a485-7a686beff5b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install e2b duckduckgo-search"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cc0cba70-8507-498d-92ac-fe47133db200",
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "\n",
    "from langchain.tools import DuckDuckGoSearchRun, E2BDataAnalysisTool\n",
    "\n",
    "tools = [E2BDataAnalysisTool(api_key=getpass.getpass()), DuckDuckGoSearchRun()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "91e6973d-3d9a-477f-99e2-4aaad16004ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = OpenAIAssistantRunnable.create_assistant(\n",
    "    name=\"langchain assistant e2b tool\",\n",
    "    instructions=\"You are a personal math tutor. Write and run code to answer math questions. You can also search the internet.\",\n",
    "    tools=tools,\n",
    "    model=\"gpt-4-1106-preview\",\n",
    "    as_agent=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78fa9320-06fc-4cbc-a3cf-39aaf2427080",
   "metadata": {},
   "source": [
    "#### Using AgentExecutor\n",
    "\n",
    "The OpenAIAssistantRunnable is compatible with the AgentExecutor, so we can pass it in as an agent directly to the executor. The AgentExecutor handles calling the invoked tools and uploading the tool outputs back to the Assistants API. Plus it comes with built-in LangSmith tracing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e38007a4-fcc1-419b-9ae4-70d36c3fc1cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'content': \"What's the weather in SF today divided by 2.7\",\n",
       " 'output': \"The search results indicate that the weather in San Francisco is 67 °F. Now I will divide this temperature by 2.7 and provide you with the result. Please note that this is a mathematical operation and does not represent a meaningful physical quantity.\\n\\nLet's calculate 67 °F divided by 2.7.\\nThe result of dividing the current temperature in San Francisco, which is 67 °F, by 2.7 is approximately 24.815.\",\n",
       " 'thread_id': 'thread_hcpYI0tfpB9mHa9d95W7nK2B',\n",
       " 'run_id': 'run_qOuVmPXS9xlV3XNPcfP8P9W2'}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import AgentExecutor\n",
    "\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools)\n",
    "agent_executor.invoke({\"content\": \"What's the weather in SF today divided by 2.7\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db6b9cbf-dd54-4346-be6c-842e08756ccc",
   "metadata": {},
   "source": [
    ":::tip\n",
    "\n",
    "[LangSmith trace](https://smith.langchain.com/public/6750972b-0849-4beb-a8bb-353d424ffade/r)\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bf4199a-eed1-485a-8da3-aed948c0e1e2",
   "metadata": {},
   "source": [
    "#### Custom execution\n",
    "\n",
    "Or with LCEL we can easily write our own execution loop for running the assistant. This gives us full control over execution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "357361ff-f54d-4fd0-b69b-77689f56f40e",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = OpenAIAssistantRunnable.create_assistant(\n",
    "    name=\"langchain assistant e2b tool\",\n",
    "    instructions=\"You are a personal math tutor. Write and run code to answer math questions.\",\n",
    "    tools=tools,\n",
    "    model=\"gpt-4-1106-preview\",\n",
    "    as_agent=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "864e7f9b-0501-4bb7-8aad-a7aa19b601af",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.schema.agent import AgentFinish\n",
    "\n",
    "\n",
    "def execute_agent(agent, tools, input):\n",
    "    tool_map = {tool.name: tool for tool in tools}\n",
    "    response = agent.invoke(input)\n",
    "    while not isinstance(response, AgentFinish):\n",
    "        tool_outputs = []\n",
    "        for action in response:\n",
    "            tool_output = tool_map[action.tool].invoke(action.tool_input)\n",
    "            print(action.tool, action.tool_input, tool_output, end=\"\\n\\n\")\n",
    "            tool_outputs.append(\n",
    "                {\"output\": tool_output, \"tool_call_id\": action.tool_call_id}\n",
    "            )\n",
    "        response = agent.invoke(\n",
    "            {\n",
    "                \"tool_outputs\": tool_outputs,\n",
    "                \"run_id\": action.run_id,\n",
    "                \"thread_id\": action.thread_id,\n",
    "            }\n",
    "        )\n",
    "\n",
    "    return response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "5ad6bb07-aac4-4b71-9e67-cc177fcbc537",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "e2b_data_analysis {'python_code': 'result = 10 - 4 ** 2.7\\nprint(result)'} {\"stdout\": \"-32.22425314473263\", \"stderr\": \"\", \"artifacts\": []}\n",
      "\n",
      "\\( 10 - 4^{2.7} \\) equals approximately -32.224.\n"
     ]
    }
   ],
   "source": [
    "response = execute_agent(agent, tools, {\"content\": \"What's 10 - 4 raised to the 2.7\"})\n",
    "print(response.return_values[\"output\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fd9f9c0-4b07-4f71-a784-88ee7bd4b089",
   "metadata": {},
   "source": [
    "## Using existing Thread\n",
    "\n",
    "To use an existing thread we just need to pass the \"thread_id\" in when invoking the agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f55a3a3a-8169-491e-aa15-cf30a2b230df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "e2b_data_analysis {'python_code': 'result = 10 - 4 ** 2.7 + 17.241\\nprint(result)'} {\"stdout\": \"-14.983253144732629\", \"stderr\": \"\", \"artifacts\": []}\n",
      "\n",
      "\\( 10 - 4^{2.7} + 17.241 \\) equals approximately -14.983.\n"
     ]
    }
   ],
   "source": [
    "next_response = execute_agent(\n",
    "    agent,\n",
    "    tools,\n",
    "    {\"content\": \"now add 17.241\", \"thread_id\": response.return_values[\"thread_id\"]},\n",
    ")\n",
    "print(next_response.return_values[\"output\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b97ee01-a657-452c-ba7f-95227ec7056e",
   "metadata": {},
   "source": [
    "## Using existing Assistant\n",
    "\n",
    "To use an existing Assistant we can initialize the `OpenAIAssistantRunnable` directly with an `assistant_id`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "08ef6ef5-e8bc-4c69-882d-65273655f6a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = OpenAIAssistantRunnable(assistant_id=\"<ASSISTANT_ID>\", as_agent=True)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "poetry-venv",
   "language": "python",
   "name": "poetry-venv"
  },
  "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
