Chat

OpenAI’s documentation provides thorough information about both the authentication process and how to set up calls.

External page: OpenAI API reference

To initializing and using calls from OpenAI, even for testing, you need to be on a paid OpenAI plan with billing correctly set up. You also need to generate an API key and set up authentication.

One of ChatGPT's core features is the Chat. This is essentially the API version of what you experience when you use OpenAI's own . When you send a request to OpenAI's server, it includes a message, and the server responds with a generated text.

Example: If you send a request with the message "Hello", ChatGPT might reply "Hi there, how are you today?"

The request can contain more data to tailor the the response to what you need, such as, setting the , providing more context, and a log of the conversation so far.

If you visit the OpenAI's API reference for the chat object (listed at the top of the article), you can see an example of what a request may look like:

In the expandable box below, we go through each part of the request to explain what they do. While it's not essential to grasp every detail to set up the call, having an understanding of these elements can be beneficial. It helps you better comprehend the mechanics behind the process, ensuring you're more informed about how the API works and why certain steps are necessary.

The request step-by-step
  • Line: curl https://api.openai.com/v1/chat/completions

    • Part: curl

    • Description: Curl is a tool for initiating calls in the . In Bubble, it's not needed for this process.

    • Part: https://api.openai.com/v1/chat/completions Description: This is the specific API we're trying to reach.

  • Line: -H “Content-Type: application/json”

    • Part: -H

    • Description: This indicates that what follows is part of the header of the call.

    • Part: Content-Type:

    • Description: Specifies the format of the response.

    • Part: application/json

    • Description: Indicates the response format is JSON.

The section below is the authentication needed for the call. The API Connector simplifies this process by adding automatic authentication for each API service. This means authentication details are applied to every related call automatically. We've explained this feature in the Authentication article, so you don't need to add these details to each call you make in Bubble.

  • Line: -H "Authorization: Bearer $OPENAI_API_KEY"

    • Part: -H

    • Description: Again, signifies header information in the call.

    • Part: Authorization: Bearer $OPENAI_API_KEY

    • Description: Authorizes the call using a , where $OPENAI_API_KEY is replaced by your actual . This setup is already explained in the authentication chapter.

We cover the structure of the messages later in the article.

Setting up the API call

Importing the cURL

The API Connector in Bubble includes a feature that allows for the direct import of a request. This tool can automatically configure the imported request to set up an API call correctly. Essentially, you can take the cURL command provided by a service like OpenAI, import it into Bubble's API Connector, and the relevant information will be appropriately mapped out and set up for use.

To import the cURL into Bubble, follow these steps:

1. Copy the original request

First, visit the documentation link provided at the top of this article. There you will find the text in the screenshot above. In the top row, make sure it is set to curl before you copy the text by clicking the copy icon or selecting the text and clicking Ctrl/Cmd + C.

2. Import the cURL request into the API Connector

Now, open up the API Connector in your Bubble app, and navigate to the API you set up. You will need to go through the steps outlined in the Authentication article before you set up the call, so that we can authenticate correctly.

Click the Import another call from cURL link, marked in red in the above screenshot. A popup will open, where you can paste the text from OpenAI's documentation:

Before we can the call, we need to edit a few details.

  1. The call will automatically be given the name cURL Call. Give it a suitable name, such as ChatGPT chat. This doesn't affect the call, but makes it with in Bubble.

  2. You'll see that the call includes the Authorization in the header. You can remove this line from the code, since the API Connector automatically handles header authorization in the API settings. To be specific, remove this line from the code: -H "Authorization: Bearer $OPENAI_API_KEY"

Initializing

After importing the cURL and removing the unnecessary call, you can initialize it.

When you click Import, the API Connector sends the call to OpenAI, which returns a response. Bubble will show you this response, and allow you to change the data type for each value. You don't need to change anything here.

Adding parameters

The initialization process involves sending some necessary data to the API call. To start a chat, after all, we need to send something that the chat bot can respond to. As you prepare to use this functionality in your app, you'll need to set this up as a dynamic value, so that you or your users can insert the value that you want to send.

Below is a quick introduction to the different properties in this call. For a more in-depth guide, see OpenAI's own article linked below.

Each property of the JSON consists of a key-value pair. For example, the first property has the key model and the value gpt-3.5-turbo.

{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "Hello!"
        }
    ]
}
Understanding roles in ChatGPT

Request

System

The system role is "your app". It lets you add context or instructions to the call that helps ChatGPT understand how to responds. In the example above, the system tells OpenAI to "be a helpful assistant", which sets the tone of the conversation. Whatever content you send here is often not visible to the user.

You are an expert on the solar system, and will talk about nothing else.

User

This is the user of your app, and the "person" ChatGPT responds to. In many cases, your app's users will provide the content of these messages.

Is Pluto a planet?

Response

Assistant

The final role is not visible in the JSON properties above (as it's part of the response), but is any text that OpenAI has sent back as a response to the user's message. If you include messages from the assistant in the call, ChatGPT will consider it a part of earlier conversation and take in into account as context. Including the chat history is important to make ChatGPT give consistent, conversational responses that void repetition.

No, Pluto is not a planet.

Replacing static text with parameters

First, let's have another look at the <body> we sent over to OpenAI during initialization:

{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "Hello!"
        }
    ]
}

Let's look at how the code changes, when we want to replace some static values with dynamic ones.

{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "system",
            "content": "You are an expert on  and will only answer questions about this subject."
        },
        {
            "role": "user",
            "content": "I want to learn about ."
        }
    ]
}

As you can see, we can place parameter wherever we want in the JSON body, by wrapping the name of the parameter in <>. Bubble will automatically mark the parameters in green, and set up fields for these parameters below the JSON field:

To test that ChatGPT respects the instructions we provided in the content given by the system role, we purposely set the topic to sunflowers, a topic that contains the word sun, but is still unrelated to the solar system.

Re-initalizing

Click Reinitalize call to run it one more time. In our case, OpenAI sent us this response:

{
  "id": "CHAT_ID",
  "object": "chat.completion",
  "created": 1710161438,
  "model": "gpt-3.5-turbo-0125",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "I can provide information about the astronomical object known as the sun, but sunflowers are actually flowering plants and are not related to the sun in space. Would you like to know about the sun instead?"
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 36,
    "completion_tokens": 40,
    "total_tokens": 76
  },
  "system_fingerprint": "SYSTEM-FINGERPRINT"
}

As you can see in the response, ChatGPT sent the following message in return:

I can provide information about the astronomical object known as the sun, but sunflowers are actually flowering plants and are not related to the sun in space. Would you like to know about the sun instead?

This shows that while ChatGPT understands the concept of sunflowers (meaning that its knowledge is not technically restricted only to the subject of the solar system), it will politely remind the user that the topic of conversation is restricted to the solar system. This means chat ChatGPT successfully received the message from both the user role and the system role.

Dynamic parameters

Until this point, we have only sent ChatGPT static text directly from the API Connector's settings. Of course, for ChatGPT to really be useful, we need to replace these values with dynamic ones, such as user input or the results of .

This is what we've done so far:

  • Set up parameters in the code by wrapping their key names in <>

  • Added in the API Connector

  • Initialized the call

Now, let's look at how we can use these parameters in our app.

The API Connector

To make sure that the API Connector accepts a dynamic parameter value, we need to change the setting on each of them to unchecked.

The design tab

Next, we'll start adding some elements to the page. In this example, we want the user to supply the chat messages, emulating ChatGPT's chat platform. Navigate to the and add the following:

  • An input where the user can type in a question

  • A button to start the workflow that sends the message

  • A text element to show the response

Our design looks like the below:

The workflow tab

Sending the request

Next, navigate to the . First, we'll connect a workflow to be triggered by the Send button. To find the action that sends a chat message, search for the name you gave the API call in the API Connector.

In our case, we gave it the name Send ChatGPT chat message:

Bubble will automatically we set up, and we can use a dynamic expression to send the value in the input field:

Receiving the response

When we initialized the call, Bubble learned what the response looks like. With that information, we can automatically offer a response in the same workflow that triggers the action. To do that, add a second action step to the workflow, and set it to Display data in a group/popup.

Remember that we set up a group called Group response with the content type text, and a text element that shows the parent group's text. That way, we can use the Display data in a group/popup action to send the message to the group, and set the text element's data source to parent group's text.

What we received was a neat response to the question we sent:

Save and view chat history

Our app so far can send a message to ChatGPT, and receive a response. We can see this response in a text element, as illustrated above. In some cases, this is enough. For example, if you simply want to be able to ask a simple question, and get a single response in return, you may not need to store the response at all.

However, you may want your users to be able to see a list of all the messages sent and received. To do that, we'll start leveraging the Bubble database in addition to the API.

The data tab

Don't forget to set up privacy rules on your data types, in order to safeguard any sensitive information.

Article: Protecting data with privacy rules

Create a new data type to save chat data

In order for this to work, we first need to save the data. We'll store it in the database to make sure we can load it as needed. We will use a fairly simple database setup for this, but feel free to set it up in a way that makes sense for your app. We'll set up a data type with the following fields:

Data typeField nameTypeExplanation

ChatGPT Message

Message

text

The message sent by the user or the assistant

In the data tab, our data type looks like this:

We will create one message for each message that goes to and from the API – in other words, we'll save both the request (the user's message) and the response (the assistant's message) in individual database .

The workflow tab

Setting up the workflows to store messages

The workflow we set up will consist of three actions:

Let's look in more detail what each of the actions are doing. Each of the tabs below represents one of the actions in the screenshot:

Save the user's message

The first workflow creates a new thing of the Chat data type, and we store the message that the user wrote.


Action type


Fields

Message = Input Message's Value

Go ahead and the workflow by adding a message to the input we set up, and clicking the Submit button.

Displaying the conversation

Now, to show the full conversation history to users:

  1. Set up a repeating group.

  2. Set its Data source to Do a search for ChatGPT messages.

In the Design tab, it will look something like this:

If you want to save conversations in threads, or only show messages connected to a specific user, you can set up the database and expressions to take that into account. Follow the links or scroll down to the FAQ section.

Send chat history to ChatGPT

In the last steps, we set up a system for creating one ChatGPT message thing for every message that is sent by the user and and the assistant (ChatGPT). But the changes we made are still only visible in your app – ChatGPT is still oblivious to the previous messages that we've stored in the database.

One of the key features of ChatGPT is its ability to take previous messages from both the user and the assistant into account when generating a response.

For example, if we asked the question "Is Pluto a planet", we could follow up with the question "How far is it from the sun?", and ChatGPT would use the context to understand that "it" refers to Pluto from the first message.

ChatGPT doesn't actually remember the chat history, but requires that you send the history along with every call if you want it to be considered when the response is generated. This is optional, and the expandable box below highlights some of the things you can consider before you decide to include it or not.

Do I have to send chat history?

Sending the chat history along with a ChatGPT request is optional. While it can be highly useful in some cases, it's not given that you should include it. The different scenarios and considerations below can help you decide.

Sending no chat history

If you want to send generic, isolated queries where context isn't needed, like asking for a standalone fact, joke or something else.

Sending the last message only or selected messages

If you're continuing a conversation but only need the last message or specific key messages for context, like following up on a specific query from earlier in the conversation.

Sending the full chat history

Ideal for ongoing discussions where previous context is crucial, such as a technical support chat where the issue evolves over time and each response builds on the previous interaction.

When we sent the first call to ChatGPT, we included the JSON object messages. We sent a total of two messages (one from the role system and one from the role user). Sending a chat history is essentially just about including the additional messages in the same list/array.

This is the original JSON we sent earlier:

{
    "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "Hello!"
      }
    ]
  }

Notice the after "messages": and at the second bottom row. They denote the beginning and end of the messages object. Each object within this array is then enclosed in .

Let's extend that JSON code a bit to see what it would look like after some back and forth between the user and assistant. We're just looking at this as a en example for now; you don't need to do anything with the code.

{
    "model": "gpt-3.5-turbo",
    "messages": [
      {
        "role": "system",
        "content": "You are an expert on <subject> and will only answer questions about this subject.."
      },
      {
        "role": "user",
        "content": "Hello!"
      },
      {
        "role": "assistant",
        "content": "Hi! How can I help you today?"
      },
      {
        "role": "user",
        "content": "How many planets are there in our solar system?"
      },
      {
        "role": "assistant",
        "content": "There are eight planets in our solar system."
      }
    ]
}

A few things worth noting:

  • We're not changing the structure of the JSON in any way; we're simply adding more messages to the existing code, separated by a comma and wrapped in curly brackets. In essence, we're just making the list longer.

    • This means that each message should look like this: {"role": "user","content": "Your message here."},

  • The message from system remains the same, since this is the basic behavioural instruction we want ChatGPT to follow, and it shouldn't change within the same thread of conversation.

  • We're choosing to send messages from both the user, and the response from the assistant, so that ChatGPT has access to the entire context. This will also help ChatGPT avoid repeating itself.

Now that we know how we want the JSON to look, we're going to make the necessary changes in our app to generate that JSON and include it in the call.

The API Connector

First, we'll need to adjust the parameters we set up earlier. The reason we're doing this, is that the original setup only supports sending one message, and now we want to send a JSON-formatted list of messages.

To do that, we're going to replace the entire value in the messages object with a dynamic parameter, except for the square brackets that mark the start and end of the array. You can call this parameter whatever you like, but in our example, we'll call it message-list, to make it clear that we're expecting an array:

{
    "model": "gpt-3.5-turbo",
    "messages" : [<message-list>]
}

Note that the square brackets [] are part of the JSON, while the angle brackets <> instructs Bubble that we want to place a parameter within the code.

In the API Connector, the Body now looks like this:

Reinitializing the call

Now, since the dynamic parameters have changed (we've removed subject and topic and replaced it with message-list), we need to re-initialize the call for Bubble to learn and update the parameters.

For this, we need to provide a test value in the value field that corresponds with the message-list key. For that, you can use the test code below:

{
        "role": "system",
        "content": "You are an expert on astronomy and will only answer questions about this subject."
    },
    {
        "role": "user",
        "content": "Hello!"
    },
    {
        "role": "assistant",
        "content": "Hi! How can I help you today?"
    },
    {
        "role": "user",
        "content": "How many planets are there in our solar system?"
    },
    {
        "role": "assistant",
        "content": "There are eight planets in our solar system."
}

Then, click the Reinitalize call button to send it, and wait for the response. If successful, the new message-list parameter will have replaced the two old parameters. Let's head over to the workflow tab to check it.

The data tab

Secondly, we'll need to make some additions to the ChatGPT data type. Let's first explore why.

Our purpose here is to send a list of previous messages to ChatGPT, including the new one. To do that, we need to supply ChatGPT with some simple for it to understand details that are not visible in the chat message itself: the role of each message.

By including the role in the message, we're letting ChatGPT know who sent what.

The role key-value-pair, as you may remember, is a part of the JSON, the common language that helps the API Connector to the ChatGPT API speak with each other. We're going to set up one more field on the ChatGPT message data type:

Data typeField nameTypeExplanation

ChatGPT Message

Message

text

The simple text message that your users see

Message JSON

text

The same text, formatted as JSON

The data type in the data tab should now look like this:

That's it for the data tab. Next, we'll edit the workflow and expressions to generate the JSON properly.

The workflow tab

Returning to the workflow tab, we're going to make some changes to the workflows.

Save the user's message

The first workflow creates a new thing of the Chat data type, and we store the message that the user wrote.

In the Message JSON field we just created, we need to add a static text string in front of the dynamic value from the input field:

{"role": "user", "content":

You'll notice that we didn't close the curly bracket – that's because we need to add that after the dynamic content:

}

You can see what the entire field should look like under Fields:


Action type


Fields

Message = Input Message's Value

Message JSON = {"role": "user", "content":Input Chat message's value:}


Screenshot

Before you test this workflow, you may need to delete ChatGPT database things already in your database, since they don't contain the JSON field we added in this section. This may lead to an error in the API, since we'll be

With that set up, you can test your app again. Click Preview and send the first chat message. Then, try sending another one to see how ChatGPT handles the context.

FAQ: OpenAI ChatGPT

I am getting the error "You exceeded your current quota, please check your plan and billing details"

The cause of this error is most likely in your OpenAI settings, and can be displayed for a few different reasons. Please visit the article below in the OpenAI documentation to see a list of possible causes:

External page: Error Code 429 | OpenAI Documentation

I set up dynamic parameters in my call, but when I try using it in a workflow, Bubble doesn't show the parameters.

This may be because the parameters are to private. This is their default setting, and it hides the value of the parameter from your users. For it to be dynamic and usable in your app, you need to uncheck Private on each parameter in the call you want to send.

Bubble is showing some of the parameters I added, but not new ones

Keep in mind that for Bubble to show you the right parameters, you need to re-initialize the call for Bubble to learn the JSON and the response from the API.

To initialize a call, follow the instructions earlier in this article.

When I add the call to an action, Bubble asks me to fill out Content-type on each call. Why is this necessary?

If you imported the call from a cURL, the Content-type is automatically added to each call. There are two ways you can stop Bubble from asking each time you set up an action:

  • In the settings for the relevant call in the API Connector, locate the Headers section and the parameter with they key Content-type. Check the Private to change the parameter to non-dynamic and hidden.

  • To apply the header automatically to all calls, you can move it from the call to the Authorization settings of the parent API provider. Locate the section called Shared header for all calls, and copy/paste the key and value there.

What is a "role" in ChatGPT?

The role specifies different parts of the conversation. In essence, "who says what". You can read a more detailed description here.

What is a "token" in ChatGPT?

In the context of NLP (Natural Language Processing) as used in AI models like ChatGPT, tokens are essentially the basic elements of text, serving as the building blocks for these models to understand and interpret language. Each token represents a piece of textual data, making it easier for the model to analyze and process natural language efficiently.

Tokens are divided into different types:

  1. Word tokens: Represent individual words.

  2. Punctuation tokens: Represent punctuation marks like periods and exclamation marks.

  3. Number tokens: Symbolize numbers, for instance, "100" or "2500".

  4. Special tokens: Used for structure, such as marking sentence ends or indicating new paragraphs.

ChatGPT takes the message you send, and breaks it into tokens. This helps the model understand the meaning of the text and generate more accurate responses, as well as saving system resources.

By segmenting text into smaller parts or tokens, a model can more effectively learn and understand the statistical relationships between these tokens. This process is crucial in building a more accurate and efficient model for natural language processing, as it simplifies the complexities of language into manageable, analyzable elements.

For you as an app builder, knowing about tokens is useful, as it can help you do things like setting a maximum response length.

What does "hallucination" mean in ChatGPT?

Hallucinations is the term used to describe that LLMs can fabricate facts and present them in a convincing manner, even if they are incorrect. This is why OpenAI discourages trusting ChatGPT to always provide factually correct responses.

Can I control how creative or precise ChatGPT is? | What is temperature?

When studying the ChatGPT API documentation, you may have come across the temperature parameter. This is an optional parameter that tells ChatGPT how deterministic or random the response should be.

Temperature follows a range from 0 to 2, and supports . The default value is 1.

⬆ a higher value means that the response will be more random. This can be useful for creative tasks, such as storytelling.

⬇ a lower value means the response will be more deterministic and precise. This is better for when you need correct factual responses. Keep in mind that ChatGPT can .

External page: OpenAI API Reference | Temperature

Can I instruct ChatGPT to avoid repeating tokens? | What is frequency_penalty?

When studying the ChatGPT API documentation, you may have come across the frequency_penalty parameter. This is an optional parameter that tells ChatGPT how strict it should be about repeating a certain token in the response.

Frequency penalty requires a range from -2.0 and 2.0, and supports . The default value is 0.

⬆ a higher value means that ChatGPT will avoid repeating tokens in the response, to avoid a repetitive text. Keep in mind that avoiding repetition is not always a good thing. For example, if you discuss the subject of "Bubble", you'll want ChatGPT to be able to repeat that token and stick to the subject.

⬇ a lower value means that ChatGPT will not avoid repetition, and will stick strictly to the subject being discussed. This can be used to increase the likelihood of repetition.

External page: OpenAI API Reference | Frequency penalty External page: OpenAI API Reference | Frequency penalty explainer

Can I set the length of ChatGPT's response? | What is max_tokens?

Yes, ChatGPT supports setting a maximum number of tokens. In essence (although the actual calculation is a bit more complex, see previous link), this instructs ChatGPT to keep the number of words in a response to a set value.

The max_tokens parameter lets you assign a number value to the total number of tokens that can be generated in the chat completion.

Keep in mind:

  • Tokens don't represent an actual word count – they also include things like spaces, punctuation and paragraph changes

  • Setting a maximum number of tokens can help you control the cost of using the API, by avoiding overly lengthy responses

I can't see any chat messages in my repeating group

If the messages are not showing in the repeating group, try the following:

  • Check the Data tab to see if the things have been successfully created

  • Check the data source on the repeating group that it doesn't contain any errors or unnecessary constrains

  • Check that the data is not hidden by

  • Check the for any issues

How can I show the conversation history to look like a traditional chat (like WhatsApp or iMessage?

If you have set up repeating group to display all the chat messages, you may want to change the formatting of it to show in a visual way who said what.

You can format this in a few different ways, such as:

  • Including a header in the message that shows the role of the "speaker"

  • Assign different colors to the assistant and user roles

  • Set alignment based on role, such as user messages on the right and assistant messages on the left

Whichever type of formatting you choose, you can solve this by adding another field to the ChatGPT message data type, that includes the role of the "speaker".

This can be a text field that simply reflects the role parameter as they appear in the ChatGPT request and response, or you can use an with the two roles set up as separate options.

How can I make sure that chat history only shows messages that belong to a specific conversation, as opposed to all messages ever created?

You may want to save isolated conversations in your app. For example, you may want to separate the chat history between different users, so that one user only has access to their own chat history, or even that each user can keep track of multiple threads.

Keep in mind that ChatGPT doesn't save your conversations in a way that lets you load and display the conversation later – but you can easily set this up using the Bubble database and the ChatGPT message data type we created earlier in this article.

Separating messages by user

To filter out messages created by the same user, you can use Bubble's built-in Created by field. Access the Do a search for data source in the repeating group you created, and add a constraint:

Created by = Current user

Separating by conversations

If you have used ChatGPT's own web app, you may be used to seeing chat history neatly organized into conversations that are stored permanently.

To do the same thing in Bubble, you can add a new data type called Conversation, and a new field to the ChatGPT message data type that links to the Conversation data type. Save the conversation on each message, and finally set up a constraint on the repeating group that filters by the new field. In the example below, we're showing how you can use a dropdown to allow for conversation selection:

Conversation = Dropdown Conversation's value

Last updated