Happy path: Building a Chatbot in Vaadin with OpenAI
Tags: openai, vaadin, java, tutorial
Published: 2023-04-11 14:12:54

---
title: Happy path: Building a Chatbot in Vaadin with OpenAI
published: true
description: 
tags: openai, vaadin, java, tutorial
canonical_url: https://vaadin.com/blog/building-a-chatbot-in-vaadin-with-openai
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-11 17:05 +0000
---

Want to build better version of ChatGPT? Here is how to use OpenAI's [Chat completions API](https://platform.openai.com/docs/guides/chat/chat-completions-beta) to add a robot participant to your [Vaadin](https://vaadin.com/flow) chat app.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yord4c6nho6uo9f7ha8z.png)

## Create a new chat app project
Using the service at [start.vaadin.com](https://start.vaadin.com/), create a new Vaadin application. There actually is a complete chat example template, but the "hello world" sample is enough for this.

## Create a simple chat UI 
Using [MessageList](https://vaadin.com/docs/latest/components/message-list) and [MessageInput](https://vaadin.com/docs/latest/components/message-input) from Vaadin, create a simple chat UI:

```java
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: 

```java
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, add a submit listener to the MessageInput: 
```java
input.addSubmitListener(this::onSubmit);
```
and implement the method:
```java
private void onSubmit(MessageInput.SubmitEvent submitEvent) {
    List<OpenAI.Message> messages =
            openAI.send(submitEvent.getValue());     
    chat.setItems(messages.stream()
            .map(this::convertMessage)
            .collect(Collectors.toList()));
}
```
That combined with straight-forward conversion from Message to MessageListItem: 

```java
private MessageListItem convertMessage(OpenAI.Message msg) {
    return new MessageListItem(msg.getContent(),
                            msg.getTime(),
                            formatName(msg.getRole()));
}
```

## Chatting asynchronously
To make the chat behave more real-time, few steps are needed
Enable websockets by adding [@Push in Application class](https://vaadin.com/docs/latest/advanced/server-push) and to 
use OpenAI.sendAsync instead to return a [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)

**Note:** to update the UI in the asynchronous callback, wrapping it into a UI.access call.

```java
@Push
public class Application implements AppShellConfigurator {
    // ...
}
```

```java
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()));
        });
    });
```

## Conclusion
That was nice. Vaadin components provide a nice familiar look and feel, and you can easily customize them to your own needs. The complete source code is again available on GitHub for your (and AI's) inspiration: [github.com/samie/vaadin-openai-chat](https://github.com/samie/vaadin-openai-chat)

Just update the [openai.apikey](https://platform.openai.com/account/api-keys) in the `application.properties` and you are good to try it yourself. 

Continuing from here, you can guess where this is heading if you already saw my [voice activation tips for Vaadin](https://dev.to/samiekblad/always-listening-voice-commands-for-vaadin-web-applications-3bhp).