OpenAI API Chatbot for ChatGPT

Go on an adventure into AI! In this tutorial, you’ll use the OpenAI ChatGPT API to create a chatbot in Android and create your own experience with this user-friendly machine learning technology. By arjuna sky kok.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Talk to Me: Mimic the Back-and-Forth

To display the message from ChatGPT, remove the code you added in the ChattingScreen.kt file below the line // TODO: Create dialog boxes, then add the following code:

      val dummyMessages = listOf("Hello, AI!", "Hi, Human!")
      LazyColumn(
        modifier = Modifier
          .fillMaxHeight(),
        verticalArrangement = Arrangement.Bottom
      ) {
        itemsIndexed(dummyMessages) { index, messageBox ->
          if (index==0) {
            UserMessage(messageBox)
          } else {
            AIMessage(messageBox)
          }
        }
      }

Build and run the project, and you see the following screen:

The AI message

This looks good. The AI message box is aligned to the left, and the user message box is aligned to the right. They have different colors for the box and message. Both chat message boxes are bottom aligned. This is because of the Arrangement.Bottom value for the verticalArrangement property of the LazyColumn function. Currently, these are merely dummy chat messages that you printed. But you want the user message to pop up after the user types a message, followed by the AI message.

Artificial, for Real

It’s time to add a callback to the chat window button to display the user message.

Remove the code you added below the line // TODO: Create dialog boxes. Then, replace it with the following code:

      LazyColumn(
        modifier = Modifier
          .fillMaxHeight(),
        verticalArrangement = Arrangement.Bottom
      ) {
        itemsIndexed(historicalMessages) { index, messageBox ->
          if (messageBox.role == MessageType.USER.type) {
            UserMessage(messageBox.content)
          } else if (messageBox.role == MessageType.ASSISTANT.type) {
            AIMessage(messageBox.content)
          }
        }
      }

You don’t need to use dummy messages anymore. You used historicalMessages, which is a mutableListOf. The Message class itself was defined in ChatModel.kt like this:

data class Message(
  val role: String,
  val content: String
)

This is part of the payload data you’ll send to the OpenAI API server. If the role attribute of the Message data is user, then it’s the message that the user types. If the role attribute of the Message data is assistant, then it’s the message that comes from the server. The content attribute refers to the message itself.

So in the button’s callback, you merely need to add messages to the historicalMessages variable. Add the following code below the line // TODO: Add dialog boxes to the chatting window in the ChattingScreen.kt:

            
historicalMessages.add(Message("user", message))
val aiMessage = "Hello! This is an automated message generated by an AI assistant. I'm here to assist you with any tasks or inquiries you may have. Please let me know how I can be of assistance!"
historicalMessages.add(Message("assistant", aiMessage))

You added the message to historicalMessages and then added an AI dummy message to historicalMessages to simulate the reply from ChatGPT.

Build and run the project, then type something in the TextField before clicking the button. When “Hello, AI!” is entered into the TextField and sent, Chatty Bot sends the dummy message in response, and you see a screen that looks something like this:

Message from the OpenAI API server

Creating Typing Animation

Before creating calls to the OpenAI API server, you’ll enhance the appearance of the ChatGPT response. If you’ve used ChatGPT, you’ve noticed that the reply doesn’t show up instantly; it’s presented as if ChatGPT is typing the text to you.

You’ll use LaunchedEffect to achieve this same type of animation on the Compose function. To create the animation, you need to think of two texts. The first one is the complete reply text from ChatGPT. The second one is the partial text. At the beginning, the partial text is empty. Then, after a delay, the partial text is the first character of the full text. Then, after another delay, the partial text is the first two characters of the full text. This process continues until the partial text becomes the full text.

Put this process inside the LaunchedEffect. The partial text and the full text have been set up for you. They’re the partText variable as the partial text and the addedString variable as the full text.

Delete the lines of code you added in the ChattingScreen.kt file below the line // TODO: Create dialog boxes and replace it with the following code:

      LazyColumn(
        modifier = Modifier
          .fillMaxHeight(),
        verticalArrangement = Arrangement.Bottom
      ) {
        itemsIndexed(historicalMessages) { index, messageBox ->
          if (messageBox.role == MessageType.ASSISTANT.type && index == historicalMessages.size - 1) {
            AIMessage(partText)
          } else if (messageBox.role == MessageType.USER.type) {
            UserMessage(messageBox.content)
          } else if (messageBox.role == MessageType.ASSISTANT.type) {
            AIMessage(messageBox.content)
          }
        }
      }

Animate Text Output

It’s the same response as before, except for the last message. If the message comes from ChatGPT, you want to set the content with the partText variable. To update the partial text so it becomes the full text, add the following code below the line // TODO: Add the animation of typing text to the dialog box:

    addedString.forEachIndexed { charIndex, _ ->
      partText = addedString.substring(startIndex = 0, endIndex = charIndex + 1)
      delay(50)
    }

Here, you update the partText variable by getting a substring of the full text, addedString. You add some delay to create the typing simulation.

Last, you need to set the addedString variable with the dummy reply message from ChatGPT. Go to the line // TODO: Add dialog boxes to the chat window and add the following code below the declaration of the aiMessage variable:

  addedString = aiMessage

Build and run the project, then type a message before clicking the button. You see the typing animation:

If you look at the LaunchedEffect code, you notice there’s the key argument key1:

  LaunchedEffect(key1 = addedString) {
    addedString.forEachIndexed { charIndex, _ ->
      partText = addedString.substring(startIndex = 0, endIndex = charIndex + 1)
      delay(50)
    }
  }

LaunchedEffect is a one-time event executed after the Compose UI is ready and the key arguments change. In the code above, the key argument is key1. If the key1 argument that tracks addedString changes because the OpenAI server sends a reply message, the animation is triggered. So if another reply message changes the key1 argument, it will trigger the animation again.

Creating OpenAI API Calls

Until now, you created dummy messages because your focus was on the UI. It’s time to display reply messages from ChatGPT. But first, you need to get the API key from OpenAI. Go to here to grab your OpenAI API key. Then, put it on the Settings screen and click Save:

OpenAI API key in settings

To create an API call to the OpenAI API server, the URL is https://api.openai.com/v1/chat/completions and the content type is JSON. Next, put the API key in the authorization header.

No coding is required regarding this. You can find the URL in the BASE_URL variable that has the value https://api.openai.com/v1/ in the ChatRetriever.kt file. The baseURL method of Retrofit.Builder uses the variable’s value.

You’ll find the rest of the API URL, chat/completions, in the annotation of the getChatCompletions method in OpenAIService. The JSON content type is also set here.

But what JSON payload do you need to send to the OpenAI API server? You’ll send a JSON object that has several keys, like model and prompt. An example would be:

{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "Hello AI!"}
  ]
}

That’s the basic message you need to send to the server. You’ll use the GPT-3.5 model because that’s the most popular one available to the general public, though as of this tutorial’s completion, OpenAI is releasing the GPT-4 model. However, due to high demand, OpenAI is granting access to the GPT-4 API model at a measured pace. The GPT-4 model has a more extensive knowledge base and an enhanced ability to reason, but it’s more expensive.

If you want to use the GPT-4 model, change the "gpt-3.5-turbo" string to the "gpt-4" string in ChatRetriever.kt.