{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "85fb2c03-ab88-4c8c-97e3-a7f2954555ab",
   "metadata": {},
   "source": [
    "# OpenAPI Agent\n",
    "\n",
    "This notebook showcases an agent designed to interact with an OpenAPI spec and make a correct API request based on the information it has gathered from the spec.\n",
    "\n",
    "In the below example, we are using the OpenAPI spec for the OpenAI API, which you can find [here](https://github.com/openai/openai-openapi/blob/master/openapi.yaml)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "893f90fd-f8f6-470a-a76d-1f200ba02e2f",
   "metadata": {},
   "source": [
    "## Initialization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ff988466-c389-4ec6-b6ac-14364a537fd5",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import yaml\n",
    "\n",
    "from langchain.agents import create_openapi_agent\n",
    "from langchain.agents.agent_toolkits import OpenAPIToolkit\n",
    "from langchain.llms.openai import OpenAI\n",
    "from langchain.requests import RequestsWrapper\n",
    "from langchain.tools.json.tool import JsonSpec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9ecd1ba0-3937-4359-a41e-68605f0596a1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "with open(\"openai_openapi.yml\") as f:\n",
    "    data = yaml.load(f, Loader=yaml.FullLoader)\n",
    "json_spec=JsonSpec(dict_=data, max_value_length=4000)\n",
    "headers = {\n",
    "    \"Authorization\": f\"Bearer {os.getenv('OPENAI_API_KEY')}\"\n",
    "}\n",
    "requests_wrapper=RequestsWrapper(headers=headers)\n",
    "openapi_toolkit = OpenAPIToolkit.from_llm(OpenAI(temperature=0), json_spec, requests_wrapper, verbose=True)\n",
    "openapi_agent_executor = create_openapi_agent(\n",
    "    llm=OpenAI(temperature=0),\n",
    "    toolkit=openapi_toolkit,\n",
    "    verbose=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f111879d-ae84-41f9-ad82-d3e6b72c41ba",
   "metadata": {},
   "source": [
    "## Example: agent capable of analyzing OpenAPI spec and making requests"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "548db7f7-337b-4ba8-905c-e7fd58c01799",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction: json_explorer\n",
      "Action Input: What is the base url for the API?\u001b[0m\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction: json_spec_list_keys\n",
      "Action Input: data\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['openapi', 'info', 'servers', 'tags', 'paths', 'components', 'x-oaiMeta']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the servers key to see what the base url is\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"servers\"][0]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mValueError('Value at path `data[\"servers\"][0]` is not a dict, get the value directly.')\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should get the value of the servers key\n",
      "Action: json_spec_get_value\n",
      "Action Input: data[\"servers\"][0]\u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3m{'url': 'https://api.openai.com/v1'}\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I now know the base url for the API\n",
      "Final Answer: The base url for the API is https://api.openai.com/v1\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "Observation: \u001b[33;1m\u001b[1;3mThe base url for the API is https://api.openai.com/v1\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should find the path for the /completions endpoint.\n",
      "Action: json_explorer\n",
      "Action Input: What is the path for the /completions endpoint?\u001b[0m\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction: json_spec_list_keys\n",
      "Action Input: data\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['openapi', 'info', 'servers', 'tags', 'paths', 'components', 'x-oaiMeta']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the paths key to see what endpoints exist\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['/engines', '/engines/{engine_id}', '/completions', '/edits', '/images/generations', '/images/edits', '/images/variations', '/embeddings', '/engines/{engine_id}/search', '/files', '/files/{file_id}', '/files/{file_id}/content', '/answers', '/classifications', '/fine-tunes', '/fine-tunes/{fine_tune_id}', '/fine-tunes/{fine_tune_id}/cancel', '/fine-tunes/{fine_tune_id}/events', '/models', '/models/{model}', '/moderations']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I now know the path for the /completions endpoint\n",
      "Final Answer: data[\"paths\"][2]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "Observation: \u001b[33;1m\u001b[1;3mdata[\"paths\"][2]\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should find the required parameters for the POST request.\n",
      "Action: json_explorer\n",
      "Action Input: What are the required parameters for a POST request to the /completions endpoint?\u001b[0m\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction: json_spec_list_keys\n",
      "Action Input: data\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['openapi', 'info', 'servers', 'tags', 'paths', 'components', 'x-oaiMeta']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the paths key to see what endpoints exist\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['/engines', '/engines/{engine_id}', '/completions', '/edits', '/images/generations', '/images/edits', '/images/variations', '/embeddings', '/engines/{engine_id}/search', '/files', '/files/{file_id}', '/files/{file_id}/content', '/answers', '/classifications', '/fine-tunes', '/fine-tunes/{fine_tune_id}', '/fine-tunes/{fine_tune_id}/cancel', '/fine-tunes/{fine_tune_id}/events', '/models', '/models/{model}', '/moderations']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the /completions endpoint to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['post']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the post key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['operationId', 'tags', 'summary', 'requestBody', 'responses', 'x-oaiMeta']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the requestBody key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['required', 'content']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the content key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['application/json']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the application/json key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['schema']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the schema key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"][\"schema\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['$ref']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the $ref key to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"][\"schema\"][\"$ref\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mValueError('Value at path `data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"][\"schema\"][\"$ref\"]` is not a dict, get the value directly.')\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the $ref key to get the value directly\n",
      "Action: json_spec_get_value\n",
      "Action Input: data[\"paths\"][\"/completions\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"][\"schema\"][\"$ref\"]\u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3m#/components/schemas/CreateCompletionRequest\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the CreateCompletionRequest schema to see what parameters are required\n",
      "Action: json_spec_list_keys\n",
      "Action Input: data[\"components\"][\"schemas\"][\"CreateCompletionRequest\"]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m['type', 'properties', 'required']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I should look at the required key to see what parameters are required\n",
      "Action: json_spec_get_value\n",
      "Action Input: data[\"components\"][\"schemas\"][\"CreateCompletionRequest\"][\"required\"]\u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3m['model']\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I now know the final answer\n",
      "Final Answer: The required parameters for a POST request to the /completions endpoint are 'model'.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "Observation: \u001b[33;1m\u001b[1;3mThe required parameters for a POST request to the /completions endpoint are 'model'.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I now know the parameters needed to make the request.\n",
      "Action: requests_post\n",
      "Action Input: { \"url\": \"https://api.openai.com/v1/completions\", \"data\": { \"model\": \"davinci\", \"prompt\": \"tell me a joke\" } }\u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3m{\"id\":\"cmpl-6oeEcNETfq8TOuIUQvAct6NrBXihs\",\"object\":\"text_completion\",\"created\":1677529082,\"model\":\"davinci\",\"choices\":[{\"text\":\"\\n\\n\\n\\nLove is a battlefield\\n\\n\\n\\nIt's me...And some\",\"index\":0,\"logprobs\":null,\"finish_reason\":\"length\"}],\"usage\":{\"prompt_tokens\":4,\"completion_tokens\":16,\"total_tokens\":20}}\n",
      "\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I now know the final answer.\n",
      "Final Answer: Love is a battlefield. It's me...And some.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"Love is a battlefield. It's me...And some.\""
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "openapi_agent_executor.run(\"Make a post request to openai /completions. The prompt should be 'tell me a joke.'\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ec9582b",
   "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.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
