OpenAPI is a common format for describing APIs. Many apps and services will come with an OpenAPI definition file that describes the endpoints and operations that are available. Each operation consists of a path on the server, an HTTP method (such as GET or POST), and sometimes parameters or a request body.
One feature that was recently added to GPTScript allows you to provide an OpenAPI definition file to GPTScript, as though it were a normal tool file. Internally, GPTScript will generate a tool for each operation in the OpenAPI definition, and when the large language model calls that tool, GPTScript will make the correct HTTP request to the server, and return the contents of the response body to the LLM. This makes it easy to interact with APIs using LLMs like GPT-4. This tutorial will demonstrate how to use this feature.
The Definition
This is the OpenAPI definition file that we will use:
# petstore.yaml
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://127.0.0.1:5000
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
maximum: 100
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
maxItems: 100
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
This is adapted from the Swagger Petstore example.
This file defines two paths and three operations. Create this file on your system and name it "petstore.yaml".
The Server
Here is a small Python Flask app that serves this API:
# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
# Mock data for demonstration purposes
pets = [
{"id": 1, "name": "Rex", "tag": "dog"},
{"id": 2, "name": "Mittens", "tag": "cat"}
]
@app.route('/pets', methods=['GET'])
def list_pets():
limit = request.args.get('limit', default=100, type=int)
return jsonify(pets[:limit])
@app.route('/pets', methods=['POST'])
def create_pet():
new_pet = request.json
pets.append(new_pet) # Simple storage for demonstration, would typically save to a database
return jsonify({}), 201
@app.route('/pets/<int:petId>', methods=['GET'])
def show_pet_by_id(petId):
pet = next((pet for pet in pets if pet['id'] == petId), None)
if pet:
return jsonify(pet)
else:
return jsonify({"code": 404, "message": "Pet not found"}), 404
# Error handling for the API
@app.errorhandler(404)
def not_found(error):
return jsonify({"code": 404, "message": "Not found"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"code": 500, "message": "Internal server error"}), 500
if __name__ == '__main__':
app.run(debug=True)
Create this file on your system and name it "app.py". If you do not have Flask installed, you can install it by
running "pip install flask" (or "pip3 install flask", depending on how Python is installed on your system).
Start the server by running "flask run" in the directory containing the app.py file. The server will start on http://localhost:5000.
Using the API in GPTScript
Now we will use GPTScript to interact with this API. Export the environment variable OPENAI_API_KEY with your OpenAI key if you have not done so already. Next, let’s write a small script to test this out:
# petstore.gpt
tools: ./petstore.yaml
List all the pets.
Save this file and call it "petstore.gpt". It should be in the same directory as the petstore.yaml OpenAPI definition.
Then, run the script with "gptscript petstore.gpt". Your output should look something like this:
The pets are:
1. Rex (dog)
2. Mittens (cat)
This shows that GPTScript made the correct API call to list the pets. Now let’s ask it to add a new pet with ID 3, and then list all the pets again:
# petstore.gpt
tools: ./petstore.yaml
Only make one function call at a time.
Add a new pet with ID 3. His name is Mark and he is a Lizard. After you do that, list all the pets.
The output should look something like this:
The pets listed are:
1. Rex (Dog)
2. Mittens (Cat)
3. Mark (Lizard)
The only operation we have not used yet is the "showPetById" operation. Let’s try it:
# petstore.gpt
tools: ./petstore.yaml
Which pet has ID 3?
The output should look something like this:
The pet with ID 3 is named Mark and has the tag "Lizard".
That brings us to the end of this tutorial. For more details about using OpenAPI definitions with GPTScript, see
the docs. If you’d like more information about using OpenAPI with Acorn, we also hosted a livestream where we discussed it in more detail, which I’ve embedded below.
<iframe width=”560″ height=”315″ src=”https://www.youtube.com/embed/FvIdZbun1KM?si=33svLqMoacPB7I2n” title=”YouTube video player” frameborder=”0″ allow=”accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share” referrerpolicy=”strict-origin-when-cross-origin” allowfullscreen></iframe>