Blog

Building an OpenAI-powered chatbot in Java

By  
Sami Ekblad
Sami Ekblad
·
On Aug 12, 2024 11:31:00 AM
·

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.

Image description

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.propertiesand 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.

Sami Ekblad
Sami Ekblad
Sami Ekblad is one of the original members of the Vaadin team. As a DX lead he is now working as a developer advocate, to help people the most out of Vaadin tools. You can find many add-ons and code samples to help you get started with Vaadin. Follow at – @samiekblad
Other posts by Sami Ekblad