Updated in August 2024.
Want to build a better version of ChatGPT? Learn how to integrate OpenAI's Chat Completions API to add an intelligent chatbot to your Vaadin Flow chat application.
Create a new chat app with OpenAI chatbot integration
Using the service at start.vaadin.com, create a new Vaadin application. A complete chat example template is available, but the "hello world" sample is enough.

Create a simple chat UI
Using MessageList and MessageInput from Vaadin Flow, create a simple chat UI
public class ChatView extends VerticalLayout {
private MessageList chat;
private MessageInput input;
public ChatView() {
chat = new MessageList();
input = new MessageInput();
add(chat, input);
input.addSubmitListener(this::onSubmit);
}
}
And to make everything align nicely:
this.setHorizontalComponentAlignment(Alignment.CENTER,
chat, input);
this.setPadding(true); // Leave some white space
this.setHeightFull(); // We maximize to window
chat.setSizeFull(); // Chat takes most of the space
input.setWidthFull(); // Full width only
chat.setMaxWidth("800px"); // Limit the width
input.setMaxWidth("800px");
Create OpenAI Java API to call
The next step is the interesting one. There is a Java API for OpenAI, but can ChatGPT create a simple REST API to call itself from Java? It took a few steps to get right, but yes, it can. Here is what I asked so you get the idea...
Write a simple Java class called "OpenAI" to call completions to chat API with one "send" method that takes the latest user input as a parameter and returns updated chat messages. Provide inner classes "ChatRequest" and "ChatResponse" and use Jackson ObjectMapper for communication.
[ ... ]
That code uses the basic completion API, not the chat completion API endpoint at api.openai.com/v1/chat/completions. Can you adjust the code?
[ ... ]
Change the return value of send to be a List of instances of inner class "ChatMessage" instead of an array of strings.
[ ... ]
Change the ChatResponse class to match the following JSON:
[ ... ]
Write a sendAsync method that calls the OpenAI.send method asynchronously.
[ ... ]
And so on. Eventually, with some manual tweaking, here we are. Just the way I wanted it; Spring Component, very readable code.
Make the Vaadin app chat with you
To call the OpenAI's chat endpoint synchronously is simple. Just add a submit listener to the MessageInput:
input.addSubmitListener(this::onSubmit);
and implement the method:
private void onSubmit(MessageInput.SubmitEvent submitEvent) {
List<OpenAI.Message> messages =
openAI.send(submitEvent.getValue());
chat.setItems(messages.stream()
.map(this::convertMessage)
.collect(Collectors.toList()));
}
Combine the above with a straight-forward conversion from Message to MessageListItem:
@Push
public class Application implements AppShellConfigurator {
// ...
}
Chatting asynchronously
To make the chat behave more real-time, a few steps are needed
Enable WebSockets by adding @Push in the Application class and
use OpenAI.sendAsync to return a CompletableFuture.
Note: to update the UI in the asynchronous callback, wrap it into a UI.access call.
private void onSubmit(MessageInput.SubmitEvent submitEvent) {
openAI.sendAsync(submitEvent.getValue())
.whenComplete((messages, t) -> {
// Lock the Vaadin UI for updates
getUI().get().access(() -> {
chat.setItems(messages.stream()
.map(this::convertMessage)
.collect(Collectors.toList()));
});
});
Final thoughts
Vaadin components provide a nice, familiar look and feel, and you can easily customize them to your own needs. The complete source code is available on GitHub for your (and AI's) inspiration: github.com/samie/vaadin-openai-chat
Just update the openai.apikey in application.properties, and you are good to try it yourself.
Continuing from here, you can guess where this is heading if you have already seen my voice activation tips for Vaadin.