diff --git a/examples/Chained_function_calling_with_APISpec.ipynb b/examples/Chained_function_calling_with_APISpec.ipynb new file mode 100644 index 00000000..05ae7476 --- /dev/null +++ b/examples/Chained_function_calling_with_APISpec.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"8317718bcd8b475e9b336514241d679e","deepnote_cell_type":"text-cell-h1"},"source":"# Chained function calling with OpenAPI Spec","block_group":"915440d3c8d2439bb021e661c89cc652"},{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"6f810c4e582c41a1bd3bdfc4263dee89","deepnote_cell_type":"text-cell-p"},"source":"This notebook demonstrates a straightforward way to chain function calls together. We have created a fake OpenAPI spec for \"froge\" characters using `gpt-4`. We then transform the OpenAPI spec into a set of functions that can be supplied to the Chat completion API. The model intelligently chooses to output a JSON object containing arguments to call those functions. The Chat completions API does not call the function; instead, the model generates JSON you can use to call the function.","block_group":"6dacc2f716ed41f189a258c3a0f6eb9a"},{"cell_type":"markdown","metadata":{"formattedRanges":[{"url":"https://platform.openai.com/docs/guides/gpt/function-calling","type":"link","ranges":[],"toCodePoint":146,"fromCodePoint":133}],"cell_id":"5bb03753fd404e589994cc118b1a447e","deepnote_cell_type":"text-cell-p"},"source":"This notebook contains the following 2 sections. First, we convert a sample OpenAPI spec into a list of functions and arguments. See API Reference. Then we take that list of functions, a user instruction, and execute the functions sequentially.","block_group":"12951b72993b4afbba9f3528f0f914c0"},{"cell_type":"code","metadata":{"source_hash":null,"execution_start":1697176493606,"execution_millis":4812,"deepnote_to_be_reexecuted":false,"cell_id":"bf983b3e199d4ea6a2718e58a141bd88","deepnote_cell_type":"code"},"source":"!pip install -q jsonref\n!pip install -q openai","block_group":"f8d6b1292af340c4872f7de9a8403f82","execution_count":null,"outputs":[{"name":"stdout","text":"\n\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.2.1\u001b[0m\n\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n\n\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.2.1\u001b[0m\n\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n","output_type":"stream"}]},{"cell_type":"code","metadata":{"source_hash":null,"execution_start":1697179780830,"execution_millis":7,"deepnote_to_be_reexecuted":false,"cell_id":"12fb12583cc74b7c842a1b4656b94f47","deepnote_cell_type":"code"},"source":"import os\nimport json\nimport jsonref\nimport openai\n\nopenai.api_key = os.environ[\"OPENAI_API_KEY\"]","block_group":"12fb12583cc74b7c842a1b4656b94f47","execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"76ae868e66b14cc48c9c447302ea268e","deepnote_cell_type":"text-cell-h2"},"source":"## Parsing OpenAPI spec and converting it into OpenAI functions","block_group":"66fa4382c524452e870abda7cc6e3c80"},{"cell_type":"markdown","metadata":{"formattedRanges":[{"url":"https://gist.githubusercontent.com/shyamal-anadkat/d44674a87778796222bdb8fa9158ad47/raw/030d173d53c55d806a93976705cf1c5f9e9c5240/frogeapi.json","type":"link","ranges":[],"toCodePoint":31,"fromCodePoint":19}],"cell_id":"bd5ed7755eb344329afcf03209a913fb","deepnote_cell_type":"text-cell-p"},"source":"Let's parse a fake OpenAPI spec into function specifications for the OpenAI Chat completion API. ","block_group":"d38cf75869894b6bb857f08bcb9cec57"},{"cell_type":"code","metadata":{"source_hash":null,"execution_start":1697180635697,"execution_millis":329,"deepnote_to_be_reexecuted":false,"cell_id":"adbb17ca8a2a4fa2aa3f0213a0e211b6","deepnote_cell_type":"code"},"source":"def parse_functions(openapi_spec): \n\n functions = []\n\n # iterate through the paths and methods specified in the OpenAPI spec\n for path, methods in openapi_spec[\"paths\"].items():\n for method, spec_with_ref in methods.items():\n spec = jsonref.replace_refs(spec_with_ref)\n function_name = spec.get(\"operationId\")\n \n # gather the description, request body and parameters from the spec\n desc = spec.get(\"description\") or spec.get(\"summary\", \"\")\n req_body = spec.get(\"requestBody\", {}).get(\"content\", {}).get(\"application/json\", {}).get(\"schema\")\n params = spec.get(\"parameters\", [])\n \n schema = {\"type\": \"object\", \"properties\": {}}\n if req_body:\n schema[\"properties\"][\"requestBody\"] = req_body\n \n if params:\n param_properties = {param[\"name\"]: param[\"schema\"] for param in params}\n schema[\"properties\"][\"parameters\"] = {\"type\": \"object\", \"properties\": param_properties}\n \n functions.append({\"name\": function_name, \"description\": desc, \"parameters\": schema})\n\n return functions\n\nurl = \"https://gist.githubusercontent.com/shyamal-anadkat/d44674a87778796222bdb8fa9158ad47/raw/030d173d53c55d806a93976705cf1c5f9e9c5240/frogeapi.json\"\nopenapi_spec = jsonref.loads(requests.get(url).content)\n\nfunctions = parse_functions(openapi_spec)\n\ndisplay(functions)","block_group":"227ed653d07f476c94ed3808ffccad57","execution_count":null,"outputs":[{"data":{"text/plain":"[{'name': 'listFroges',\n 'description': 'List all froge characters',\n 'parameters': {'type': 'object', 'properties': {}}},\n {'name': 'createFroge',\n 'description': 'Create a new froge character',\n 'parameters': {'type': 'object',\n 'properties': {'requestBody': {'type': 'object',\n 'properties': {'id': {'type': 'string'},\n 'name': {'type': 'string'},\n 'age': {'type': 'integer'}},\n 'required': ['name', 'age']}}}},\n {'name': 'getFrogeById',\n 'description': 'Retrieve a froge character by ID',\n 'parameters': {'type': 'object',\n 'properties': {'parameters': {'type': 'object',\n 'properties': {'id': {'type': 'string'}}}}}},\n {'name': 'deleteFroge',\n 'description': 'Delete a froge character by ID',\n 'parameters': {'type': 'object',\n 'properties': {'parameters': {'type': 'object',\n 'properties': {'id': {'type': 'string'}}}}}},\n {'name': 'updateFrogeName',\n 'description': \"Update a froge character's name by ID\",\n 'parameters': {'type': 'object',\n 'properties': {'requestBody': {'type': 'object',\n 'properties': {'name': {'type': 'string'}},\n 'required': ['name']},\n 'parameters': {'type': 'object',\n 'properties': {'id': {'type': 'string'}}}}}},\n {'name': 'getFrogeByName',\n 'description': 'Retrieve a froge character by name',\n 'parameters': {'type': 'object',\n 'properties': {'parameters': {'type': 'object',\n 'properties': {'name': {'type': 'string'}}}}}}]"},"metadata":{},"output_type":"display_data"}]},{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"f03f1aacdade4ed9a422797d3cf79fbb","deepnote_cell_type":"text-cell-h2"},"source":"## Chaining function calls","block_group":"9932c1fe2fb641c0ba128b4b6066a794"},{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"08712696b5fd4cafac7b4b496ee67c5a","deepnote_cell_type":"text-cell-p"},"source":"Let's pass the fake function specifications, along with a user instruction, and write simple logic for chaining those functions together sequentially. ","block_group":"9b5bf5d2a3a8448db240268b32e9441c"},{"cell_type":"code","metadata":{"source_hash":null,"execution_start":1697182455978,"execution_millis":6254,"deepnote_to_be_reexecuted":false,"cell_id":"b8f7d0f157264694b958008f93aabf3f","deepnote_cell_type":"code"},"source":"FROGE_PROMPT = \"\"\"\nYou are a helpful froge. Respond to the following prompt by using function_call.\n\nGet all the froges. Then create a new 2-year old froge named dalle3, with a random numerical id. Then look for a froge named davinci. Then delete froge with id 2456.\n\"\"\"\nmessages = [\n {\"role\": \"system\", \"content\": \"Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous.\"},\n {\"role\": \"user\", \"content\": FROGE_PROMPT}\n]\n\n# Maximum number of chained calls allowed to prevent infinite or lengthy loops\nMAX_CHAINED_CALLS = 5\n\ndef get_openai_response(functions, messages):\n return openai.ChatCompletion.create(\n model='gpt-3.5-turbo-16k-0613',\n functions=functions,\n function_call=\"auto\", # \"auto\" means the model can pick between generating a message or calling a function.\n temperature=0,\n messages=messages\n )\n\ndef process_chained_calls(functions, messages):\n stack = 0\n while stack < MAX_CHAINED_CALLS:\n response = get_openai_response(functions, messages)\n message = response[\"choices\"][0][\"message\"]\n \n if message.get(\"function_call\"):\n display(f\"Function call number: {stack + 1}\")\n display(message)\n messages.append(message)\n stack += 1\n else:\n display(message)\n break\n \n if stack >= MAX_CHAINED_CALLS:\n display(f\"Reached max chained function calls: {MAX_CHAINED_CALLS}\")\n\nprocess_chained_calls(functions, messages)","block_group":"a9b683e7b54c43838e747b4d660581f1","execution_count":null,"outputs":[{"data":{"text/plain":"'Function call number: 1'"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":" JSON: {\n \"role\": \"assistant\",\n \"content\": null,\n \"function_call\": {\n \"name\": \"listFroges\",\n \"arguments\": \"{}\"\n }\n}"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":"'Function call number: 2'"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":" JSON: {\n \"role\": \"assistant\",\n \"content\": null,\n \"function_call\": {\n \"name\": \"createFroge\",\n \"arguments\": \"{\\n \\\"requestBody\\\": {\\n \\\"id\\\": \\\"1234\\\",\\n \\\"name\\\": \\\"dalle3\\\",\\n \\\"age\\\": 2\\n }\\n}\"\n }\n}"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":"'Function call number: 3'"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":" JSON: {\n \"role\": \"assistant\",\n \"content\": null,\n \"function_call\": {\n \"name\": \"getFrogeByName\",\n \"arguments\": \"{\\n \\\"parameters\\\": {\\n \\\"name\\\": \\\"davinci\\\"\\n }\\n}\"\n }\n}"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":"'Function call number: 4'"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":" JSON: {\n \"role\": \"assistant\",\n \"content\": null,\n \"function_call\": {\n \"name\": \"deleteFroge\",\n \"arguments\": \"{\\n \\\"parameters\\\": {\\n \\\"id\\\": \\\"2456\\\"\\n }\\n}\"\n }\n}"},"metadata":{},"output_type":"display_data"},{"data":{"text/plain":" JSON: {\n \"role\": \"assistant\",\n \"content\": \"Here are the steps you requested:\\n\\n1. Get all the froges.\\n2. Create a new 2-year old froge named dalle3 with a random numerical id.\\n3. Look for a froge named davinci.\\n4. Delete the froge with id 2456.\\n\\nPlease note that I have executed the function calls, but I haven't received any specific responses from the server.\"\n}"},"metadata":{},"output_type":"display_data"}]},{"cell_type":"markdown","metadata":{"formattedRanges":[],"cell_id":"bee0c08cc21545cab564e0d0f2f567a2","deepnote_cell_type":"text-cell-p"},"source":"","block_group":"9d36679fc8bf4cf2a2d65a32716ec5ad"},{"cell_type":"markdown","metadata":{"deepnote_img_src":"image-20231013-013652.png","cell_id":"634cd194d312436fa8dc414ee4eba511","deepnote_cell_type":"image"},"source":"","block_group":"044e3ce55be141f1bb067379569e3252"},{"cell_type":"markdown","source":"\nCreated in deepnote.com \nCreated in Deepnote","metadata":{"created_in_deepnote_cell":true,"deepnote_cell_type":"markdown"}}],"nbformat":4,"nbformat_minor":0,"metadata":{"deepnote":{},"orig_nbformat":2,"deepnote_notebook_id":"84d101406ec34e36a9cf96d0c0c25a7d","deepnote_persisted_session":{"createdAt":"2023-10-12T23:01:45.778Z"},"deepnote_execution_queue":[]}} \ No newline at end of file