In our previous tutorial on building ChatGPT Plugins, we walked through the absolute basics a build a simple to-do list plugin running on localhost.
In this article, we'll expand on these basic building blocks and build a new ChatGPT plugin: namely, an assistant that fetches and summarizes the latest news and articles on AI and machine learning.
Plugins are tools designed specifically for language models with safety as a core principle, and help ChatGPT access up-to-date information, run computations, or use third-party services.
For this new plugin, we'll host our plugin at Replit and use News API to search and retrieve relevant AI news.
The three main files we'll need to build this plugin include:
- A
main.py
to serve the API's functionality - A plugin manifest for the metadata called
ai-plugin.json
- and an
openapi.yaml
file to document the API for ChatGPT
As you can see, this plugin allows you to specify a topic or query within that you want AI news for, as well as the time frame and several other parameters:
Here we can also see the request and response that's being made to our AI news assistant:
{
"query": "ChatGPT",
"from": "2023-04-17",
"to": "2023-04-24",
"sortBy": "publishedAt",
"pageSize": 5
}
Alright now that we know what we're building, let's get started with building the API functionality.
Step 1: Building the APIs functionality
To start, let's head over to Replit, create a new Python Repl, and create our main.py
to serve our APIs functionality and the other required files.
Imports and constants
First, we'll import the required packages, initialize our Flask app, and define two constants for the News API URL and API key.
import os
import logging
from logging import StreamHandler
from waitress import serve
from flask import Flask, request, jsonify, send_from_directory, make_response
import requests
from datetime import datetime, timedelta
app = Flask(__name__)
NEWS_API_URL = "https://newsapi.org/v2/everything"
NEWS_API_KEY = "your-api-key"
Setting up logging
We'll also setup some basic logging configurations to keep track of events & errors for development purposes:
app.logger.handlers.clear()
handler = StreamHandler()
handler.setLevel(logging.INFO)
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)
app.logger.info("Application started")
Fetch news articles
Next, we'll define a function called fetch_news_articles
that will send a request to the News API return relevant articles in JSON format. This function also takes in paremeters such as the user's query, data rate, sorting, and page size.
def fetch_news_articles(query,
from_date=None,
to_date=None,
sortBy="publishedAt",
page=1,
pageSize=10):
params = {
"q": query,
"apiKey": NEWS_API_KEY,
"sortBy": sortBy,
"page": page,
"pageSize": pageSize,
"language": "en",
}
if from_date:
params["from"] = from_date
if to_date:
params["to"] = to_date
response = requests.get(NEWS_API_URL, params=params)
if response.status_code == 200:
result = response.json()
return result["articles"]
else:
raise Exception(f"Error: {response.status_code}, {response.text}")
API route for fetching news
Next, let's define an API route for handling the user-defined query parameters, calling the fetch_news_articles
function and returning relevant articles:
@app.route('/news', methods=['GET'])
def fetch_news():
app.logger.info("Fetching news")
query = request.args.get('query', "AI")
from_date = request.args.get('from', None)
to_date = request.args.get('to', None)
sortBy = request.args.get('sortBy', "publishedAt")
page = request.args.get('page', 1)
pageSize = request.args.get('pageSize', 10)
try:
articles = fetch_news_articles(query, from_date, to_date, sortBy, page,
pageSize)
return jsonify(articles)
except Exception as e:
return jsonify({"error": str(e)}), 400
Serving plugin files
Now that we've got our News API functions in place, we'll need three routes to serve the necessary ChatGPT plugin files, including the ai-plugin.json
, openapi.yaml
and the plugin logo.
First, we'll define a route that to serve our plugin manifest file, which is located in the .well-known
folder
@app.route('/.well-known/ai-plugin.json')
def serve_ai_plugin():
app.logger.info("Serving ai-plugin.json")
return send_from_directory('.well-known',
'ai-plugin.json',
mimetype='application/json')
We'll also define a route that serves the OpenAPI specification file as follows:
@app.route('/.well-known/openapi.yaml')
def serve_openapi_yaml():
app.logger.info("Serving openapi.yaml")
return send_from_directory('.', 'openapi.yaml', mimetype='text/yaml')
Then we'll define a route that serves the plugin's logo:
@app.route('/logo.png')
def serve_logo():
app.logger.info("Serving logo.png")
return send_from_directory('.', 'logo.png', mimetype='image/png')
Lastly, we can use Waitress, a lightweight WSGI server, to start our Flask application and handle incoming web requests for the ChatGPT plugin:
if __name__ == '__main__':
serve(app, host="0.0.0.0", port=8080)
Step 2: Creating a plugin manifest file
Alright now that we've got our main.py
file, let's create our ai-plugin.json
file, which needs to be placed in the .well-known
folder. The manifest provides information about your plugin, such as:
- Name & description for both the model and humans
- Authentication type, for this example we'll set it to
none
for now - API configiruation - here you just need to update the
url
to your Repl URL:https://your-repl-url.repl.co/.well-known/openapi.yaml
- You can also update the URL of your logo here
https://your-repl-url.repl.co/logo.png
{
"schema_version": "v1",
"name_for_human": "AI News Assistant",
"name_for_model": "aiNews",
"description_for_human": "Plugin for fetching the latest AI news articles. You can search news with custom queries, dates, and sorting options.",
"description_for_model": "Plugin for fetching the latest AI news articles. You can search news with custom queries, dates, and sorting options.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "https://your-repl-url.repl.co/.well-known/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "https://your-repl-url.repl.co/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "http://www.example.com/legal"
}
Step 3: Documenting the API
Okay next let's create our OpenAPI specification, which is used to describe and document our API so ChatGPT can understand the app, including metadata about the title, description, and version.
- Next you'll need to update the
url
parameter with your Replit URLhttps://your-repl-url.username.repl.co
. - We then specify the
/news
endpoint and its customizable parameters includinguery
,from
,to
,sortBy
,page
, andpageSize
. - The components section then defines the schema for JSON responses
openapi: 3.0.1
info:
title: AI News ChatGPT Plugin
description: A plugin that connects to the News API to provide the latest AI news articles. Users can search news with custom queries, dates, and sorting options.
version: 'v1'
servers:
- url: https://your-repl-url.username.repl.co
paths:
/news:
get:
operationId: fetchNews
summary: Get the latest AI news articles
parameters:
- name: query
in: query
description: The search query to filter news articles
required: false
schema:
type: string
- name: from
in: query
description: The starting date for the search (format yyyy-mm-dd)
required: false
schema:
type: string
format: date
- name: to
in: query
description: The ending date for the search (format yyyy-mm-dd)
required: false
schema:
type: string
format: date
- name: sortBy
in: query
description: The sorting option for the search results (e.g., publishedAt)
required: false
schema:
type: string
- name: page
in: query
description: The page number for the search results
required: false
schema:
type: integer
- name: pageSize
in: query
description: The number of articles per page
required: false
schema:
type: integer
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/fetchNewsResponse'
"400":
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/errorResponse'
components:
schemas:
fetchNewsResponse:
type: object
properties:
articles:
type: array
items:
type: object
properties:
title:
type: string
description: The title of the news article
url:
type: string
description: The URL of the news article
publishedAt:
type: string
description: The publication date of the news article (format yyyy-mm-ddTHH:mm:ssZ)
errorResponse:
type: object
properties:
error:
type: string
description: An error message describing the issue
Step 4: Running the ChatGPT Plugin
With these three files created, we're now ready to run and install the plugin. First, you'll just need to:
- Import your project into Replit, or create a new Replit and upload your files.
- Click "Run" in Replit, which will start your server and make your plugin available.
- Go to ChatGPT and navigate to the Plugin store
4. Select "Develop your own plugin" and enter the URL of your running Replit project.
Now, you can enable the new AI News Assistant plugin in your installed plugins dropdown:
After enabling the new plugin, let's go ahead and test it out within ChatGPT and search for generative AI news:
Looks good!
Summary: Building a ChatGPT News Plugin
In this guide, we saw how to setup a simple ChatGPT Plugin news assistant for retrieving AI-related news. To recap, we:
- Created a Python Flask app with the necessary endpoints and configuration for serving our
openapi.yaml
file and ai-plugin.json file. - Defined the
ai-plugin.json
file, which contained metadata about our plugin - Documented our API using OpenAPI and outlined the available endpoints, parameters, and response schemas.
- Hosted our project on Replit and connected the plugin to ChatGPT
This example builds on our previous plugin tutorial, although there are still a few more steps to keep in mind before taking this ChatGPT plugin to production, such as rate limiting and user authentication, which we'll cover in the next few articles.