ChatGPT is a fun and useful large language model (LLM) conversational AI that, given its renown, doesn’t need much introduction. Let’s see how we can interact with the ChatGPT API service from a simple Java program.
Get started with ChatGPT
The first thing you'll need to do is to obtain an API key. You can get access to one with the free version of ChatGPT available from OpenAI. Once you're signed up, you’ll have access to the 3.5 version at no cost. (Version 4 still requires a subscription fee at the moment.)
Once you have an account and you are signed in, you can generate a new API key. Save it somewhere safe; we’ll use it later on.
Create a new Java project
Now let’s start creating a new Java application for this demo. We’ll use Maven from the command line. You can lay out a new project using the code in Listing 1.
Listing 1. Generate a new project
mvn archetype:generate -DgroupId=com.infoworld -DartifactId=java-gpt -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false -DarchetypeVersion=1.4
That will scaffold a new project in the /java-gpt
directory. Before we continue, navigate to the /java-gpt/pom.xml
file and set the Java compiler and source version to 11.
You can test this out with the command in Listing 2, which currently outputs “Hello, World!” to the console. From here on, we’ll use this command to run the application.
Listing 2. Test run the new application
mvn clean compile exec:java -Dexec.mainClass=com.infoworld.App
Set up the ChatGPT application
Let’s define exactly what our ChatGPT application will do. We’ll keep things very basic for this demo. We want to accept an input string from the user and then output the AI’s response to it on the console.
To begin, let’s frame a simple App.java
main class that accepts input. We'll define the URL and API key, which the main class will hand off to a ChatBot
class to do the real work. You can see the App.java
main class in Listing 3.
Listing 3. App.java main class
package com.infoworld;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
// Set ChatGPT endpoint and API key
String endpoint = "https://api.openai.com/v1/chat/completions";
String apiKey = "<YOUR-API-KEY>";
// Prompt user for input string
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter your message: ");
String input = reader.readLine();
// Send input to ChatGPT API and display response
String response = ChatBot.sendQuery(input, endpoint, apiKey);
LOGGER.info("Response: {}", response);
} catch (IOException e) {
LOGGER.error("Error reading input: {}", e.getMessage());
} catch (JSONException e) {
LOGGER.error("Error parsing API response: {}", e.getMessage());
} catch (Exception e) {
LOGGER.error("Unexpected error: {}", e.getMessage());
}
}
}
This class is fairly simple: It reads a line from the user with reader.readLine()
, then uses it to call the ChatBot.sendQuery()
static method. I'll show you ChatBot
in a moment. For now, make sure you set the <YOUR-API-KEY>
token to that OpenAI ChatGPT token we saved at the outset of this article. Also note that OpenAI has been making changes to the OpenAI API, so the endpoint URL of https://api.openai.com/v1/chat/completions is correct currently, but it may be different from earlier (even recent) documentation. If you get any errors, make sure the endpoint URL is is up-to-date with the latest iteration of the OpenAI API.
The ChatBot class
Notice, also, that we have configured a simple logger to watch what’s happening and track any errors. We’ll need to add some dependencies to Maven to support this class, but first, let’s look at the ChatBot
class, shown in Listing 4. For convenience, we’re just putting them both in the same package scope.
Listing 4. A simple ChatBot
package com.infoworld;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
public class ChatBot {
private static final Logger LOGGER = LoggerFactory.getLogger(ChatBot.class);
public static String sendQuery(String input, String endpoint, String apiKey) {
// Build input and API key params
JSONObject payload = new JSONObject();
JSONObject message = new JSONObject();
JSONArray messageList = new JSONArray();
message.put("role", "user");
message.put("content", input);
messageList.put(message);
payload.put("model", "gpt-3.5-turbo"); // model is important
payload.put("messages", messageList);
payload.put("temperature", 0.7);
StringEntity inputEntity = new StringEntity(payload.toString(), ContentType.APPLICATION_JSON);
// Build POST request
HttpPost post = new HttpPost(endpoint);
post.setEntity(inputEntity);
post.setHeader("Authorization", "Bearer " + apiKey);
post.setHeader("Content-Type", "application/json");
// Send POST request and parse response
try (CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(post)) {
HttpEntity resEntity = response.getEntity();
String resJsonString = new String(resEntity.getContent().readAllBytes(), StandardCharsets.UTF_8);
JSONObject resJson = new JSONObject(resJsonString);
if (resJson.has("error")) {
String errorMsg = resJson.getString("error");
LOGGER.error("Chatbot API error: {}", errorMsg);
return "Error: " + errorMsg;
}
// Parse JSON response
JSONArray responseArray = resJson.getJSONArray("choices");
List<String> responseList = new ArrayList<>();
for (int i = 0; i < responseArray.length(); i++) {
JSONObject responseObj = responseArray.getJSONObject(i);
String responseString = responseObj.getJSONObject("message").getString("content");
responseList.add(responseString);
}
// Convert response list to JSON and return it
Gson gson = new Gson();
String jsonResponse = gson.toJson(responseList);
return jsonResponse;
} catch (IOException | JSONException e) {
LOGGER.error("Error sending request: {}", e.getMessage());
return "Error: " + e.getMessage();
}
}
}
ChatBot
looks like it has a lot going on, but it's fairly simple. The biggest issue is dealing with JSON. First, we have to marshal the string input passed to sendQuery()
into the JSON format that ChatGPT expects (again, there has been some churn on this lately). ChatGPT allows for an ongoing conversation, so it looks for a collection of messages, tagged with the role of either user or assistant. The AI looks at these as the back-and-forth responses in the chat. (There is also a system role, which is used to set the “tone” of the whole conversation, although this property is inconsistently applied.) The JSON format that ChatGPT expects looks like Listing 5 (taken from the OpenAI docs).
Listing 5. Sample ChatGPT submit format JSON
messages:[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
Set up the JSON response
So, in our case, we are just using a simple set of messages, with a single role:user
and content set to the input string. We also set the model and temperature fields. The model field is important in defining which version of ChatGPT is being used. There are several options, including the newer (for pay) GPT-4. Temperature is a GPT feature that tells the AI how “creative” to be (in terms of avoiding word reuse).
With the proper JSON in hand, we use the Apache HTTP library to build up a POST
request, and set the body to the JSON (via post.setEntity()
). Notice that we set that API key on the auth
header with this line:
post.setHeader("Authorization", "Bearer " + apiKey);
Next, we use a try-with-resource
block to issue the request. With the response done (assuming we avoided any errors) we just have to navigate the JSON response structure and return the most recent message:content
.
Now we’re almost ready to test it out. First, add the necessary dependencies to the pom.xml
, as shown in Listing 6.
Listing 6. Maven dependencies in pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Now when you run the code, you should see it working. It will look something like Listing 7.
Listing 7. Talking to ChatGPT with the Java command-line application
$ mvn clean compile exec:java -Dexec.mainClass=com.infoworld.App
Enter your message: This is the forest primeval.
22:43:35.384 [com.infoworld.App.main()] INFO com.infoworld.App - Response: "The murmuring pines and the hemlocks,\n\nBearded with moss, and in garments green,\n\nIndistinct in the twilight,\n\nStand like Druids of eld,\n\nWith voices sad and prophetic,\n\nStand like harpers hoar,\n\nWith beards that rest on their bosoms.\n\nLoud from its rocky caverns,\n\nThe deep-voiced neighboring ocean\n\nSpeaks, and in accents disconsolate\n\nAnswers the wail of the forest."
See my java-chatgpt GitHub repo for the complete application code.
Conclusion
As this article demonstrates, it's fairly easy to build a ChatGPT client in Java. The JSON handling is a bit cumbersome, but otherwise, it’s easy to see how we could readily expand the demo code show here. As a first exercise, consider extending the ChatGPT program into a REPL that allows for an ongoing conversation.