In today’s rapidly evolving conversational AI landscape, ChatGPT has emerged as a game-changer, powering intelligent and engaging interactions across countless applications. As the demand for seamless integration of this cutting-edge technology grows, developers worldwide seek to harness the power of ChatGPT in their projects. In this article, we’ll dive deep to learn how to develop a ChatGPT plugin. Whether you’re an experienced developer or an AI enthusiast, this comprehensive guide will give you the knowledge and insights needed to supercharge your next project with the conversational prowess of ChatGPT.
OpenAI plugins bridge the gap between ChatGPT and third-party applications. It fixes one of the main drawbacks of ChatGPT, that is, the inability to deliver recent information and perform actions on third-party applications. With the introduction of plugins, ChatGPT can deliver recent information without any cutoff date restrictions and perform actions on third-party applications, such as booking a flight ticket. This update opens up a lot of use cases and business growth opportunities using Generative AI.
As explained by OpenAI, ChatGPT plugins can retrieve real-time information from the internet, retrieve information from knowledge-base and perform actions on behalf of the user.
Now that you know what is possible with the ChatGPT plugins, let’s look at the folder structure and essential components required for building your plugin.
To create the plugin, you must create a folder named .well-known in your API’s domain. For example, example.com/.well-known/. The /.well-known folder must exist on your domain for ChatGPT to connect with your plugin. Without this folder, you cannot install the plugin.
Once the folder is created, you need to create 2 essential components.
Suppose you created a plugin allowing your employees to submit a leave application from ChatGPT. In this scenario, the employee can select the plugin from the ChatGPT interface and send a query requesting leave. When the query is received, ChatGPT first looks into your plugin and looks through the details in ai-plugin.json.
ai-plugin.json contains basic information about the plugin and the APIs you expose to ChatGPT. In this example, the file would contain the API URL of your HRMS application. After fetching the API details, ChatGPT checks the OpenAPI specification. It is a wrapper that sits on top of your API. It tells the model how to use the API. In this example, the model will know how to use the API to submit a leave application. The model can only access the functions you define in the OpenAPI specification. For example, it can only submit leave applications but not edit employee details. After calling the API, the model receives the raw data response and is presented to the user in natural language. For example, if the API response is ‘success,’ ChatGPT would reply to the user, saying, “I’ve submitted a leave application for you.”
The file ai-plugin.json contains basic information about the plugin and the APIs you expose to ChatGPT. Here is an example for ai-plugin.json.
{ "schema_version": "v1", "name_for_human": "Sport Stats", "name_for_model": "sportStats", "description_for_human": "Get current and historical stats for sport players and games.", "description_for_model": "Get current and historical stats for sport players and games. Always display results using markdown tables.", "auth": { "type": "none" }, "api": { "type": "openapi", "url": "PLUGIN_HOSTNAME/openapi.yaml", "is_user_authenticated": false }, "logo_url": "PLUGIN_HOSTNAME/logo.png", "contact_email": "support@example.com", "legal_info_url": "https://example.com/legal" }
The description_for_model attribute allows you to instruct the model on how to use your plugin. Overall, GPT4 can understand natural language and follow instructions. So you can put general instructions on what your plugin does and how the model should use it properly. Use natural language in a concise yet descriptive and objective tone, as shown in the example above. You can refer to the definition below for the possible options for the plugin.
FIELD | TYPE | DESCRIPTION / OPTIONS |
schema_version | String | Manifest schema version |
name_for_model | String | The name for the model will be used to target the plugin Maximum 50 characters. Ex: sportStats |
name_for_human | String | Human-readable name, such as the full company name Maximum 50 characters. Ex: Sport Stats |
description_for_model | String | This description is better tailored to the model, such as token context length considerations or keyword usage for improved plugin prompting. Maximum 8000 characters (will decrease over time) Ex: Get current and historical stats for sports players and games. Always display results using markdown tables. |
description_for_human | String | Human-readable description of the plugin Maximum 120 characters. Ex: Get current and historical stats for sports players and games. |
auth | ManifestAuth | Authentication schema |
api | Object | API specification Example; “type”: “openapi”, “url”: “example.com/openapi.yaml”, “is_user_authenticated”: false |
logo_url | String | URL used to fetch the plugin’s logo |
contact_email | String | Email contact for safety/moderation reachout, support, and deactivation |
legal_info_url | String | Redirect URL for users to view plugin information |
HttpAuthorizationType | HttpAuthorizationType | “bearer” or “basic” |
ManifestAuthType | ManifestAuthType | “none”, “user_http”, “service_http”, or “oauth” |
interface BaseManifestAuth | BaseManifestAuth | type: ManifestAuthType; instructions: string; |
ManifestNoAuth | ManifestNoAuth | No authentication required: BaseManifestAuth & { type: ‘none’, } |
ManifestAuth | ManifestAuth | ManifestNoAuth, ManifestServiceHttpAuth, ManifestUserHttpAuth, ManifestOAuthAuth |
The next step is to build the OpenAPI specification to document the API. The model in ChatGPT does not know anything about your API other than what is defined in the OpenAPI specification and manifest file. If you have an extensive API, you need not expose all functionality to the model and can choose specific endpoints. The OpenAPI specification is the wrapper that sits on top of your API.
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to HTTP APIs that allows humans and computers to discover and understand the service’s capabilities without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. There are several tools available to automatically generate the OAS for your API. A popular tool is Swagger. An OpenAPI definition can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases. In this case, it helps the model to understand the API.
When a user queries in ChatGPT relevant to the plugin, the model checks the plugin’s manifest file for ‘description_for_model’ and checks the descriptions of the endpoints in the OpenAPI specification. So, it is important to prepare the OpenAPI specification accurately so that the model can call the API for the right user queries. The OpenAPI specification tells the model about the diverse details of your API, available functions, functions’ parameters, etc. Below you can find an example of OpenAPI specification. The first code widget is an example API code, and the second code widget below is the OpenAPI specification of the below API.
import json import requests import urllib.parse import quart import quart_cors from quart import request # Note: Setting CORS to allow chat.openapi.com is required for ChatGPT to access your plugin app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com") HOST_URL = "https://example.com" @app.get("/players") async def get_players(): query = request.args.get("query") res = requests.get( f"{HOST_URL}/api/v1/players?search={query}&page=0&per_page=100") body = res.json() return quart.Response(response=json.dumps(body), status=200) @app.get("/teams") async def get_teams(): res = requests.get( "{HOST_URL}/api/v1/teams?page=0&per_page=100") body = res.json() return quart.Response(response=json.dumps(body), status=200) @app.get("/games") async def get_games(): query_params = [("page", "0")] limit = request.args.get("limit") query_params.append(("per_page", limit or "100")) start_date = request.args.get("start_date") if start_date: query_params.append(("start_date", start_date)) end_date = request.args.get("end_date") if end_date: query_params.append(("end_date", end_date)) seasons = request.args.getlist("seasons") for season in seasons: query_params.append(("seasons[]", str(season))) team_ids = request.args.getlist("team_ids") for team_id in team_ids: query_params.append(("team_ids[]", str(team_id))) res = requests.get( f"{HOST_URL}/api/v1/games?{urllib.parse.urlencode(query_params)}") body = res.json() return quart.Response(response=json.dumps(body), status=200) @app.get("/stats") async def get_stats(): query_params = [("page", "0")] limit = request.args.get("limit") query_params.append(("per_page", limit or "100")) start_date = request.args.get("start_date") if start_date: query_params.append(("start_date", start_date)) end_date = request.args.get("end_date") if end_date: query_params.append(("end_date", end_date)) player_ids = request.args.getlist("player_ids") for player_id in player_ids: query_params.append(("player_ids[]", str(player_id))) game_ids = request.args.getlist("game_ids") for game_id in game_ids: query_params.append(("game_ids[]", str(game_id))) res = requests.get( f"{HOST_URL}/api/v1/stats?{urllib.parse.urlencode(query_params)}") body = res.json() return quart.Response(response=json.dumps(body), status=200) @app.get("/season_averages") async def get_season_averages(): query_params = [] season = request.args.get("season") if season: query_params.append(("season", str(season))) player_ids = request.args.getlist("player_ids") for player_id in player_ids: query_params.append(("player_ids[]", str(player_id))) res = requests.get( f"{HOST_URL}/api/v1/season_averages?{urllib.parse.urlencode(query_params)}") body = res.json() return quart.Response(response=json.dumps(body), status=200) @app.get("/logo.png") async def plugin_logo(): filename = 'logo.png' return await quart.send_file(filename, mimetype='image/png') @app.get("/.well-known/ai-plugin.json") async def plugin_manifest(): host = request.headers['Host'] with open("ai-plugin.json") as f: text = f.read() # This is a trick we do to populate the PLUGIN_HOSTNAME constant in the manifest text = text.replace("PLUGIN_HOSTNAME", f"https://{host}") return quart.Response(text, mimetype="text/json") @app.get("/openapi.yaml") async def openapi_spec(): host = request.headers['Host'] with open("openapi.yaml") as f: text = f.read() # This is a trick we do to populate the PLUGIN_HOSTNAME constant in the OpenAPI spec text = text.replace("PLUGIN_HOSTNAME", f"https://{host}") return quart.Response(text, mimetype="text/yaml") def main(): app.run(debug=True, host="0.0.0.0", port=5001) if __name__ == "__main__": main()
The above code is a sample API code for a sports application. Below, you can find the OpenAPI specification of this code. Understanding this is important to develop your ChatGPT plugin.
openapi: 3.0.1 info: title: Sport Stats description: Get current and historical stats for sport players and games. version: 'v1' servers: - url: PLUGIN_HOSTNAME paths: /players: get: operationId: getPlayers summary: Retrieves all players from all seasons whose names match the query string. parameters: - in: query name: query schema: type: string description: Used to filter players based on their name. For example, ?query=davis will return players that have 'davis' in their first or last name. responses: "200": description: OK /teams: get: operationId: getTeams summary: Retrieves all teams for the current season. responses: "200": description: OK /games: get: operationId: getGames summary: Retrieves all games that match the filters specified by the args. Display results using markdown tables. parameters: - in: query name: limit schema: type: string description: The max number of results to return. - in: query name: seasons schema: type: array items: type: string description: Filter by seasons. Seasons are represented by the year they began. For example, 2018 represents season 2018-2019. - in: query name: team_ids schema: type: array items: type: string description: Filter by team ids. Team ids can be determined using the getTeams function. - in: query name: start_date schema: type: string description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date. - in: query name: end_date schema: type: string description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date. responses: "200": description: OK /stats: get: operationId: getStats summary: Retrieves stats that match the filters specified by the args. Display results using markdown tables. parameters: - in: query name: limit schema: type: string description: The max number of results to return. - in: query name: player_ids schema: type: array items: type: string description: Filter by player ids. Player ids can be determined using the getPlayers function. - in: query name: game_ids schema: type: array items: type: string description: Filter by game ids. Game ids can be determined using the getGames function. - in: query name: start_date schema: type: string description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date. - in: query name: end_date schema: type: string description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date. responses: "200": description: OK /season_averages: get: operationId: getSeasonAverages summary: Retrieves regular season averages for the given players. Display results using markdown tables. parameters: - in: query name: season schema: type: string description: Defaults to the current season. A season is represented by the year it began. For example, 2018 represents season 2018-2019. - in: query name: player_ids schema: type: array items: type: string description: Filter by player ids. Player ids can be determined using the getPlayers function. responses: "200": description: OK
OpenAPI specification starts with defining the specification version, the title, description, and version number. When a user asks a query in ChatGPT, the model will look at the description defined in the info section of the OpenAPI specification to determine if your plugin is relevant to the query. The model will call your API to fetch the information or perform actions if relevant.
Once you have created your API’s manifest file and OpenAPI specification, you can connect it to the ChatGPT system via the ChatGPT UI. In the ChatGPT UI, select “Develop your own plugin” and then “Install an unverified plugin.” You have to add the plugin manifest file to yourdomain.com/.well-known/ path. Once done, you can start testing your API.
Some length limits for the fields in the plugin manifest file and OpenAPI specification may change.
For subsequent changes to your plugin manifest file, you must deploy the new changes to your public site, which might take a long time. In that case, OpenAI suggests setting up a local server as a proxy for your API. This lets you quickly prototype changes to your OpenAPI spec and manifest file. When writing descriptions in description_for_model in the manifest;
In conclusion, developing a ChatGPT plugin can be a game-changer for your business, providing your customers with a seamless conversational experience that is personalized and engaging. With the right tools and guidance, creating a ChatGPT plugin that meets your unique needs, whether you want to provide customer support, generate leads, or enhance your marketing efforts, is possible. Following the steps outlined in this guide, you can develop your ChatGPT plugin and take your business to the next level. Remember to test and iterate your plugin as you go, gathering feedback and making adjustments to ensure it provides maximum value to your customers. With dedication and creativity, you can develop ChatGPT plugin that transforms how you interact with your customers and drives business success.