ChatGPT Plugins: Building an AI News Assistant

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.

As OpenAI highlights:

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 URL https://your-repl-url.username.repl.co.
  • We then specify the /news endpoint and its customizable parameters including uery, from, to, sortBy, page, and pageSize.
  • 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:

  1. Import your project into Replit, or create a new Replit and upload your files.
  2. Click "Run" in Replit, which will start your server and make your plugin available.
  3. 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:

  1. Created a Python Flask app with the necessary endpoints and configuration for serving our openapi.yaml file and ai-plugin.json file.
  2. Defined the ai-plugin.json file, which contained metadata about our plugin
  3. Documented our API using OpenAPI and outlined the available endpoints, parameters, and response schemas.
  4. 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.