How to Generate Story Ideas with Twilio SMS, Java, and OpenAI GPT-3

January 25, 2022
Written by
Diane Phan
Twilion
Reviewed by

header - How to Generate Story Ideas with Twilio SMS, Java, and OpenAI GPT-3

There are many possibilities when it comes to using OpenAI GPT-3's engine. You can build a chatbot, generate new story ideas, or translate sentences from one language to another.

With the help of the Twilio SMS API, Java, and the Spark framework, you can respond to all incoming SMS' with a text message generated by the OpenAI GPT-3 engine. This article will help you set up a Spark application with access to OpenAI's services.

Specifically, you will learn how to send generated story ideas from the OpenAI GPT-3 API directly to a mobile device via SMS using Java and Spark.

Tutorial Requirements

Log onto the Twilio Dashboard to view your active phone numbers. Click on the number you want to use to view its details. If you haven’t purchased a number yet, learn how to search for and buy a Twilio phone number.

Start a new Java project in IntelliJ IDEA

Open IntelliJ IDEA and click on Create New Project. Choose Gradle on the left hand side, check Java in the box on the right hand side, and click Next.

choose gradle option for new java project

Give your project a name such as "SMSgpt3" and click the Finish button.

After the project setup and build is complete, your project directory structure should look like the following image:

project directory for the build gradle file

Open the build.gradle file in the IDE and add the following lines inside the dependencies block:

    implementation 'com.theokanning.openai-gpt3-java:client:0.4.0'
    implementation "com.github.alexdlaird:java-ngrok:1.5.5"
    implementation group: "org.slf4j", name: "slf4j-simple", version: "1.7.32"

Since the goal of this tutorial is to chat with OpenAI GPT3's engine through SMS, it is required to implement Spark, a lightweight framework to respond to all incoming SMS. This framework is a great choice for this project because it is used for quick development of web applications

In addition, the application must be exposed publicly using ngrok. In order to fully integrate and create a client to ngrok, we need to incorporate Alex Laird's java-ngrok wrapper so that we can conveniently use ngrok in our application.

It is also necessary to include the following implementation groups in order to use Twilio and Spark to send the SMS. Add the following lines to the dependencies block:

    implementation group: "com.twilio.sdk", name: "twilio", version: "8.0.+"
    implementation group: "com.sparkjava", name: "spark-core", version: "2.9.3"

Create the bot's Java class

Expand the main subfolder inside the src folder, which is in the root directory of the project. Notice the empty java folder.

project directory for the java subfolder

Right-click on the java folder, click on the New option, and select Java Class as seen in the image below. Clicking this option will prompt you to give the class a name. Go ahead and name it "SMSgpt3".

Create a new Java class in Intellij IDEA

Delete the existing template code in the newly created file, then copy and paste the following code into the class:

import java.util.ArrayList;
import com.theokanning.openai.OpenAiService;
import com.theokanning.openai.completion.CompletionRequest;
import com.theokanning.openai.engine.Engine;
import com.theokanning.openai.completion.CompletionChoice;

import com.twilio.twiml.MessagingResponse;
import com.twilio.twiml.messaging.Body;
import com.twilio.twiml.messaging.Message;
import static spark.Spark.*;
import com.github.alexdlaird.ngrok.NgrokClient;
import com.github.alexdlaird.ngrok.protocol.CreateTunnel;
import com.github.alexdlaird.ngrok.protocol.Region;
import com.github.alexdlaird.ngrok.protocol.Tunnel;

public class SMSgpt3 {
    public static void main(String[] args) {
        port(80);
    }
}

It is important to know that the Java class' filename must reflect the class name within the file.

In order to use OpenAI in Java projects, developers must incorporate TheoKanning's openai-java community library. This library allows us to utilize the CompletionRequest and CompletionChoice classes to generate an appropriate response from the engine.

In the public SMSgpt3 class, a main method is invoked so that the code in the program starts once executed. For the time being, the ngrok HTTP tunnel will connect to the default port 80.

Establish a connection to the GPT-3 engine

The OpenAI playground allows users to explore GPT-3 (Generative Pre-trained Transformer 3), a highly advanced language model that is capable of generating written text that sounds like an actual human wrote it. This powerful model can also read a user's input and learn about the context of the prompt to determine how it should generate a response.

There are different engines to choose from when working with GPT-3. In this article, the davinci model is used because it is the most capable model, but feel free to explore other GPT-3 models for your project.

Copy and paste the following code under the line establishing port(80) in the SMSgpt3 class:

        String token = System.getenv("OPENAI_TOKEN");
        OpenAiService service = new OpenAiService(token);
        Engine davinci = service.getEngine("davinci");
        ArrayList<CompletionChoice> storyArray = new ArrayList<CompletionChoice>();
        // … 

A token variable retrieves the OPENAI_TOKEN that holds the API key configured in the project's environment variables. The token allows the Java project to create an OpenAiService object where all the magic happens. This token will be set in the upcoming section.

The generated text results are returned in an ArrayList format consisting of values from TheoKanning's CompletionChoice class. Storing results in this ArrayList object is encouraged so that you have the option to expand on the story later on if desired.

Set the OpenAI API Key in IntelliJ IDEA

As mentioned above, this project requires an API token from OpenAI. At the time of writing this article, the only way to obtain the API key is by being accepted into their private beta program.

If you have access to the Beta page, the API key can be found in the Authentication tab in the Documentation.

OpenAI Beta Documentation Authentication page with API key

The Java application will need to have access to this key, so environment variables need to be set to safely make a connection to the OpenAI GPT-3 engine.

Locate the Run tab at the top of the IntelliJ IDEA console and select Edit Configurations… in the dropdown as seen below:

edit configurations option in intellij idea

This image is reused and the project directory name does not reflect the one used in this project.

Another window displaying the "Run/Debug Configurations" will pop up with the details regarding the project. If there are no existing configurations for you to edit, click on Add New… on the left-hand side and create a new application named "SMSgpt3".

option to add a new application to edit configurations in intellij idea

In the Environment variables section, follow the sample format to set the relevant variables. In this example, the API key is named OPENAI_TOKEN.

edit the configurations to build and run the java application

This image is reused and the project directory name does not reflect the one used in this project.

Click on the Apply button, and then OK once finished.

Teach the chatbot how to write

In order to make a request to the OpenAI GPT-3 engine that is relevant to our interests, a prompt and various arguments must be supplied to teach the engine, or the "SMSgpt3" app, how to return the desired results.

Copy and paste the following code right beneath the ArrayList definition to create an instance of TheoKanning's CompletionRequest class:

            if (storyArray.size() > 0){
                System.out.println("[INFO] : Clearing arraylist");
                storyArray.removeAll(storyArray);
            }
            CompletionRequest completionRequest = CompletionRequest.builder()
                    .prompt("This is a story of Boy Meets Girl, but you should know up front, this is not a Love Story.")
                    .temperature(0.7)
                    .maxTokens(94)
                    .topP(1.0)
                    .frequencyPenalty(0.0)
                    .presencePenalty(0.3)
                    .echo(true)
                    .build();
            service.createCompletion("davinci", completionRequest).getChoices().forEach(line -> {storyArray.add(line);});
            String SMSElement = storyArray.toString();

The storyArray ArrayList will be cleared if the GPT-3 contents have been generated already.

If you played around with the OpenAI playground introduced earlier in the article, you may notice that some of the arguments here look familiar.

The prompt in this example is based on the opening line from the movie "500 Days of Summer" – one of my favorite movies by the way. Feel free to change it as you please.

After setting the value for prompt, it is recommended for the builder function to specify values, such as the temperature, to lessen the randomness in results. Another value that should be specified is maxTokens, which stands for either a word or punctuation mark to be generated. This was set to 96 so that the length of the caption will be appropriate - not too long and not too short, provide a more cohesive idea on what you can start writing about.

You can read more about the GPT-3 customization options in the Ultimate Guide to OpenAI-GPT3 Language Model or explore the OpenAI Playground for yourself.

Before the build method is executed, the echo function is added to echo back the prompt in addition to the newly created tokens. Each line generated by the OpenAI service object is appended to the ArrayList defined earlier and printed onto the console.

Write the SMS POST route for the Java class

Since this application requires input from the user in order to generate a GPT-3 output to send an SMS, a webhook needs to be created. This POST route, /sms, responds with TwiML markup for an SMS message that will be added later on.

Wrap the OpenAI GPT-3 engine generating code with the highlight lines as seen below. Don't forget to include the closing brackets at the end of the POST route:


        post("/sms", (req, res) -> {
            res.type("application/xml");
            if (storyArray.size() > 0){
                System.out.println("[INFO] : Clearing arraylist");
                storyArray.removeAll(storyArray);
            }
            CompletionRequest completionRequest = CompletionRequest.builder()
                    .prompt("This is a story of Boy Meets Girl, but you should know up front, this is not a Love Story.")
                    .temperature(0.7)
                    .maxTokens(94)
                    .topP(1.0)
                    .frequencyPenalty(0.0)
                    .presencePenalty(0.3)
                    .echo(true)
                    .build();
            service.createCompletion("davinci", completionRequest).getChoices().forEach(line -> {storyArray.add(line);});
            String SMSElement = storyArray.toString();
        });

Now when the user texts in a message, a POST request is made, and the code proceeds to clear the storyArray and generate more GPT-3 results.

But wait, this function will not work yet as there is no return statement for the TwiML to be sent back to the texter.

Add the SMS functionality to the webhook

Before closing out the /sms POST route, add the following code beneath the line that stringifies the storyArray object:

            Body body = new Body
                    .Builder(SMSElement)
                    .build();
            Message sms = new Message
                    .Builder()
                    .body(body)
                    .build();
            MessagingResponse twiml = new MessagingResponse
                    .Builder()
                    .message(sms)
                    .build();
            return twiml.toXml();

The Twilio SMS API takes the SMSElement string object to construct a TwiML response that will be sent via SMS to the cellular device.

Open the ngrok tunnel

After defining the functionality of the POST route, the ngrok client and tunnel need to be initialized.

To do that, copy and paste the following code inside the main function, below the POST route:

        final NgrokClient ngrokClient = new NgrokClient.Builder().build();
        final CreateTunnel createTunnel = new CreateTunnel.Builder()
                .build();
        final Tunnel tunnel = ngrokClient.connect(createTunnel);

The final keyword is added to restrict the variable from being modified. You can view the documentation for declaring and using the ngrok in the GitHub README.

At this point, you should have a POST route that creates an SMS with Twilio's API and a connection to a tunnel from the ngrok client. Save the program.

Run the chatbot Java ngrok application

We've nearly reached the end of the tutorial. If you need to check your code, here's my GitHub repo.

Right click on the SMSgpt3 file in the project directory and find the option to Run 'SMSgpt3.main()'. Wait a few seconds for the project to build, download the project's dependencies, and compile.

Soon, the terminal should display a link to the default port 80 as well as a randomly generated link. You may have to scroll down the output text for a bit until you find a URL similar to “https://ad7e4814affe.ngrok.io/”. Here's an example of the output you might have to scroll through:

example ngrok output from running the main java program

NOTE: Keep in mind that this URL is no longer active and only presented here for demonstration purposes.

Set up the webhook with Twilio

Copy the Forwarding URL starting with https:// and open the Twilio Console, where we will tell Twilio to send incoming message notifications with this URL.

In the Twilio Console, click on the left sidebar to explore products and find Phone Numbers. Click on the active phone number that you want to use for this project and scroll down to “Messaging”.

There, paste the URL copied from the ngrok session into the A MESSAGE COMES IN field and append /sms, since that is the endpoint. Before you click on the Save button, make sure that the request method is set to HTTP POST.

Here is my example for reference:

input the unique ngrok url webhook with a route directing to sms

Send a text message to the Twilio number

Let's make this fun and send an emoji text to the Twilio phone number on the mobile device. Wait a few seconds and you should see the TwiML message response on your device.

screenshot of the example SMS interaction between the java spark app and user

Sounds like we might have a new romantic coming of age movie in the works soon!

What's next for building Java projects?

If you're eager to build more, try expanding on the project by feeding the results to the story generating app so that it keeps adding onto the story for you. Otherwise, check out these ideas and incorporate Twilio:

Let me know what new stories GPT-3 has suggested for you, or if you find another GPT-3 project to build out!

Diane Phan is a software developer on the Developer Voices team. She loves to help beginner programmers get started on creative projects that involve fun pop culture references. She can be reached at dphan [at] twilio.com or LinkedIn.