Ask a question
You can build a ChatGPT clone in just 30 minutes using Bubble.io, which is a fantastic no code web application building platform. If you're new to this channel, my name is Matt. I'm a no code educator and a Bubble coach.
ChatGPT Clone in Bubble demo
We're going to be building together an app just like this, which you yes, the design is a little bit rough, but I built it very quickly. All of the essentials for how to build an app like this are included in this video.
Let me give you a quick demonstration. So I'm going to click on New Conversation and let's ask it, what is the capital of France? I send off my prompt, my message to OpenAI. The OpenAI API is asking for a response. Bubble is waiting, and we'll see a response pop up just below there.
The capital of France is Paris. And just like on the ChatGPT web interface, I've got all my previous conversations here. I can say, what is the largest city in Europe. I can even go down to some earlier conversations I created which demonstrates that this app's chat interface is aware of the previous messages sent in a conversation.
So, for example, here we have recommend travel destinations in Europe, and I get a list, and then I ask OpenAI to expand upon that list with the best ways of arriving at those locations. So how do we build it? Let's dive right in. First of all, you need to head over to Bubble.io and to create an account. Everything that I'm showing you in this video can be created using a free account. It's going to cost you absolutely nothing, apart from the OpenAI API. You might need to put some card details in for that. So click get started, sign up for your account, create a new Bubble app, and we're going to pick up in a moment from that blank page.
Starting with a blank Bubble app
So I'm starting here with a blank Bubble app. I've skipped all of the application onboarding and I've just arrived here at this blank page. And here is where our journey starts to building a ChatGPT clone without using any code. And I'm going to begin by going into plugins, because before I design any UI, I'm going to make sure that I have a connection through to the OpenAI API. And that's how we send requests to their service and we receive data back.
Bubble API Connector - OpenAI
To do that, I'm going to go on to Plugins, install plugins, and add new plugins. And I'm going to add the API connector. This is a plugin provided by Bubble the app. You can literally create a Bubble app that integrates in with thousands, if not hundreds of thousands of websites and services all around the world. For now, we're focusing on OpenAI. And I've got two tabs open here. I've got my OpenAI API keys. You'll need to sign up for an account. You might need to add card details, but come to this page here. You click up here and then API keys. And you can see I've got a number of API keys I've hidden because I'm using them with other projects.
And I've also got open here the OpenAI AI API reference. And I'm in Chat and Chat completion because chat is the model that is used for ChatGPT, it's used for GPT-3.5-turbo and GPT-4. And so we're going to be using that.
So I'm going to start by looking at the reference here, which is we have this endpoint and I'm going to copy and add in a call and it's post and I'm going to paste the endpoint in there. I'm going to change this to action. This is all going to be explained in this tutorial. I'm going to show you everything that's required. But an action means that it's going to form part of a workflow, meaning that when a user clicks a button, we perform this action and we get data back from it.
Let's go back into documentation. I need two header parts of my API call. I need this one here, content type, and I'm going to put them in shared header because if I set up future different API calls to OpenAI, I can combine the headers into the shared header. So we have content type, application JSON, copy and paste that.
And there's one more and I'm actually going to include that in here, which is private key in header. And so we can see we have authorization, then bearer and a space and then our API key. So I will type in here, bearer. And then I'm going to go into API keys and create a new key. I'll call this PNC test and I will be deleting this before the video goes live. Copy the key and I paste it in here, making sure that there is the word bearer space. And then my API key, the development key field, we can leave empty that. So that with a service like Stripe, which has a test portal and a live portal, you might need different API keys. But for now, we're going to put everything through the API key here.
Let's scroll down and start building our call. So you can see here that our parameters are provided here in this demo. So I'm going to copy and paste this and paste it in. And so this is saying use the model GPT-3.5-turbo, which is both the most affordable and the quickest OpenAI model currently available.
If you were to use GPT-4, you would just swap it out here. That might take well, it will take longer to get a response. Bubble currently times out a little bit and occasionally if the response takes more than say, 50 seconds to a minute. So we'll be using 3.5 turbo because it still remains very impressive.
First ChatGPT clone test
We're now at a point where we can test it. And I'll just change this to Send message and we will initialise the call and there's no errors here. This is very good. And we can in fact see here that message content. This is the bit we'd actually display back to the user is, hello there, how are you today? So I'm going to click save. And in doing that, we have successfully connected our Bubble application to OpenAI and then informed Bubble about the format. It's going to get the reply of data back in. We're at a good point now to build some UI. So in my Bubble app here in the design tab, I'm going to change this to Column, check out some of our other videos. If you're unsure about the difference between fixed row and column, we go into detail there. But it's just easier to get a more reliable design if you use rows and columns.
I'm then going to set up a repeating group. And a repeating group is a way of showing a list of data because what we're going to end up with in building our ChatGPT clone is a list of messages. So I'll start by centering this, giving it a bit of padding from the top. And we need to connect it to a data type. And we can create a data type by going into data data types. By default, we have a user data type. That's how you would manage different users, log them in, register, et cetera. But this is going to be a message and our message is going to contain I'm going to add fields to it as we go along, but it's at least going to contain text. So I can say that this will display messages and this is quite a comprehensive tutorial, but I'm not going to cover everything such as privacy rules, which you might need to go into if you wanted to launch this app commercially. But for now, we're just going to say any message in the database, we're going to show it in this repeating group here and I'm going to add in some text to display the message, say that that is a row and then say current cell's text and that will then print the message in there.
I then need a multiline input because I need some way of my user entering a message to placeholder to write your message to ChatGPT here. And then we need the button. I'm not focusing too much on the UI design here and we'll say Send, but I do like to have a little bit of padding.
So that is the rough design needed in order to clone ChatGPT. Now we need to work on workflows so that we can set actions in motion in order to send a request to the OpenAI API and to get a response back. So, to begin with, we'll say when this button is clicked, we will plug in OpenAI send message. If you're not seeing that there, there are two things that I would check. One is it's OpenAI Send message because that's what I put in the two fields here. I've also successfully initialised the call. There are no errors and yeah, that's what I would check.
So we send the call to OpenAI. Here's the content. I'm then going to create a message. I'll say text is from the multiline input. So actually this is going to come first because I want to create a message that my user is sending so that I can create that nice back and forth chat interface.
Sending dynamic data to OpenAI
I'm then going to have to adjust my API call. So I've successfully initialised it and you can see that here. But actually I want the content to be dynamic, meaning that I can replace it in the workflow, so I can replace this with message content. You can see the instructions of doing that are up here, which is to use triangle brackets. And then you get this value and I'm going to untick private and that means that I then get this message field up here. And so the message is going to contain the results for step one's, text. Because in step one I am saving the input of the multiline text field that then sends my text input from my user to OpenAI. And then after this Bubble is going to wait for a response and ChatGPT 3.5 turbo. I think that's the reason they've got Turbo and the name is very quick, but I need some way of storing that. So I'm going to create another message and this is going to be text equals the results for step two. And just because I've been using OpenAI a lot, I know where to look, which is to go into choices.
The reason that they use the term choices is because you can request OpenAI to return more than one. A choice really to give you choices of the response. I don't really see how that works with a chat interface. We're only asking for one in this instance. So I'm going to go first item and then message content. I'm then going to add a reset input here that's going to make my text box empty. And I think we have now got everything there to check. Let's hit preview.
So I can now put in a message in here such as, what is the capital of the UK? So there's my user message and then we get the OpenAI response back. So I'm going to quickly change something there, which is to change the order. So it's going to be created date descending no. So I think I'll get the most recent one at the bottom. Let's cheque that I can refresh. Okay, so then I get my response. Now, there are a few extra steps we need to take. Let me go through them. One is that one of the beautiful things about and the amazing pieces of technology that ChatGPT provides is that it is aware of the historical context of the conversation.
Historical and contextual awareness
Now, what I've created here won't actually do that and we can test it. We can say, for example, tell me somewhere to visit there. So I'm using there because I think, well, it should know London, it should know that the conversation is about the UK and London. This is just to prove you that it doesn't work. So I'm going to click send and we'll see what response comes back. Hopefully, yes, again, it's not aware of the historical context of previous messages in the conversation. Let me show you how we can do that. So, we have a much truer clone of ChatGPT.
So if we go back to the reference, we can see here that the messages is basically an array in JSON. And that means that it can contain more than one message. And the way that we get historical context into our conversation is that every call we make to OpenAI, we have to include the history with it. Let me show you how we do that. So I'm going to begin by going to data and just clearing out the messages we've got so far, because we need to add fields to them.
So we need to somehow create this part of the message every time we send a call. And to do that, we need to create a new field. And I'm going to call this field JSON and we have to format it in a way that is going to be basically identical to the syntax displayed here. So I'm going to copy that's and paste it in. So what is role? Well, role is part of what we're provided with the chat endpoint, which is either system user or assistant. When we get a reply back, that is the assistant. That is what OpenAI ChatGPT labels itself. System is a way that they've designed that if you want to put in an initial prompt to maybe set the stage saying you are a personal assistant or you are a mechanic, that's where you'd use systems. For this simple chat demonstration, we're just going to use user, sorry, that's where you'd use system. For this simple chat illustration, we're going to use user and assistant. So it's role user. And then this is where we put our text from our multiline input. What we're creating here is both a human readable version to display in our repeating group and a machine readable version to keep passing over to ChatGPT.
And I'm then going to add in here format as JSON safe and JSON is the format of the language that we're using in order to send commands back and forth with OpenAI. And you might have noticed that it makes use of some punctuation such as curly brackets and quotation marks. There has to be a process known as escaping. So that when if our user was to put curly brackets or quotation marks, speech marks into their message, that's not going to confuse and muddy the syntax. That's what JSON Safe does. And based on my experience, I'm going to remove the speech marks because JSON Safe wraps this blue statement here in speech marks, quotation marks. Anyway, I will shall get rid of the exclamation mark as well.
Then we have to do the same for the reply so that OpenAI is aware of what it has sent back to us too. So we need to create here JSON and we'll do the same thing, which is the role and this time it is assistant. And the content. Again, we'll remove the speech marks and the content is result of step three choices. This is exactly the same as up here first item message content JSON safe.
Last thing I need to do is to adjust the call we've got here, which is we need a way of listing through all of our different messages. So I'm going to change this to messages. So then my request is going to be do a search for messages and that's going to find my step one. And then I need to list through each item's JSON. And have I got curly brackets on it? Let me just check I'm now thinking, yes, I've left the curly brackets on good. So I just need to separate them by a comma. And so I do join with comma space. Now I'm going to test this. I might have got the syntax a bit wrong, but we'll try. And I'm going to of course order this by created by date no. And so that way every single time I send a message and I get a reply back, I'm building up the conversation. And that then means that each time I send the request, every step of the conversation so far is passed on to OpenAI. I think that is then ready to go. And I'm going to make a change here to demonstrate it, which is to put in a line space and JSON.
So that stands out. I'm going to use a bit of BBcode so that it's in italics. So let's try that again. Let's go for what is the capital of Australia. So you can see here that my Bubble app has generated the JSON required to send it. And then I get a response back saying, this is the capital Australia. And you can see here that my Bubble app has also generated the JSON needed to remember that response. And I can go into data. And I can see here that I've got my JSON and I've got my human readable text. So let's check now, if it is historically aware, I will say list five places to visit there. Okay, so then I get five popular places to visit in Australia. And you can see here part of the JSON safe taking effect, which is that it uses backslash N to symbolise a line break. So that the formatting that OpenAI has replied with here, which is numbers and a line break to each one is maintained when it sends that part of the conversation back to OpenAI. So that there is that full historical context. Now, the last thing I'm going to show you is how to group them by conversation.
Adding a sidebar and conversations
And so we're going to have a left hand sidebar similar to what you'll find in OpenAI. So I'm going to clear out the messages again and we'll add in a data type called conversation. And yeah, we'll leave that blank for now. And if we go back to design, we have to make a number of changes. First of all, I'm going to highlight all of these holding shift and clicking on them. And then I'm going to group these into a column. I think I clicked on the wrong thing there. Group into column. No, what am I doing wrong? Oh, it has been grouped. Right, delete that. So I have all of the content that I've been using so far of my page into a column here. And I'm going to say leave that as it is. I'm going to change the page to a row and add in a group. And this is going to be our sidebar so I could label it sidebar. I'm going to add in a bit of spacing between the elements, say 30 pixels. And then here I'm going to get rid of the width restriction. And same here, same here.
Let's take that down to 24. And I make this a column because I'm going to put another repeating group in here to go through conversations so that it's a bit different. I'm going to change the background to a grey. Then going to put in a repeating group. Give my sidebar a little bit of padding. Take my repeating group by unticking element fixed width. It makes it the full width of the space. And then going to put some text in there. This is going to be of conversations. Conversations are going to be do a search of all conversations in our database and this time conversations will be by modified date descending. So the most recently changed or updated conversation comes out at the top. Let's just change this to a column and the text we'll just conversation label. I'm going to change this in a moment, but just SEO, there's some text there. I'm going to click preview, see what difference that has made so far. Right? And then I need a button to create a new conversation.
I need to set up two additional workflows with this sidebar. One is to create a new conversation and the other is to display a previous conversation in my repeating group here.
And then also make sure that any messages that I send while that conversation is being displayed become part of that conversation. So I'm going to use this group here. I'll call this page body and type of content. I'll say conversation and that then means that I can go new conversation, start edit, workflow, create a new thing conversation. And then I can display that conversation into page body and I display the results of step one which is that conversation. That's one way of passing data between elements on my page. Now to link it all in, I need a way of connecting my messages to the conversation. So I'm going to add a field. In fact, I can't do it there. If I go into data messages, add a field conversation of type conversation. And so now I can add in a constraint and the constraint allows me to filter down my search results. So I'm going to only show messages where the conversation equals parent group's conversation. What is the parent group up here? Page body. We're setting that conversation. I then need to make a change here, which is each message that I create, the conversation has to be set.
And so I can again go parent groups conversation, parent groups conversation. So that way I'm grouping everything together under the umbrella of a conversation when it is displayed over here. If I just make this a bit bigger. And then I'm going to make the height 100% because I'm going to turn this into a button so that when this text is clicked, I will display data into the page body of current sales conversation. So that way I can determine what will be displayed in these set of elements. Now let's return back to the label ChatGPT basically gives you a hint of what a conversation is about. In this instance, I'm going to go current sales conversation. In fact, I'm going to do a search for messages where conversation equals current sales conversation, sort them by created, get the first one up, first item text. And so what that will do is it will display in my list of conversations the first message that is sent in that conversation to help identify that conversation. Now, have you noticed what isn't going to work so well here, which is that if I preview currently no conversation is visible.
And so that then means that if I was to start sending messages, they're not actually going to attach into a conversation because no conversation is available to do so. And so they wouldn't be grouped in what we've just been creating. There are a couple of different ways you could go about resolving this. I'm just going to go about it by saying on page load, when do a search for conversations count is zero. So when there are no conversations in the database. I'm going to create a conversation and I'm going to display that conversation. So that is how it would work if there were no conversations in the database. Now, what if there were? I'm going to copy and paste this, and this time say, if conversation count is not zero, I'm not going to create conversation, but I am going to display data so that we're not left with that empty void where there's no data in it. And this I'm just going to refer to the repeating group conversation. That's my sidebar lists of conversation. First item. So if there are conversations, it's going to open it up with the previous conversation that was last used.
Now, there may be some bugs to work out, but I think we're now at a good point to test it so we can say, recommend travel destinations in Europe. Okay. And there we get a big, long list back. To tidy things up. I'm going to remove the JSON expression in the text here by refresh. And you can see there that my workflow on page loads. When conversations is not zero, it has brought me back into the previous conversation. Let's test that it is still historically aware of context. So we've asked about Europe. So I could ask for I could say, let's try this. Add to each location listed the best way to travel there. Now, I don't know how OpenAI is going to respond to that, because it doesn't know where I am in the world, but let's just see if it acknowledges that there is a list. Okay. It's just taking its time. That's okay. There we go. So the best way to travel to France? Barcelona. Yes. With Copenhagen. We've got our list. Let's go for a new conversation. And so I'll say I'm still thinking of travel, but why don't I say, what are the top scientific achievements of the 21st century?
Again, we're waiting on a response. Currently, Bubble.io doesn't have the facility to stream text in, so we just have to wait until the whole amount is ready. Okay, so we've got CRISPR higgs boson, human genome synchron. Fascinating. Okay, now let's ask a follow up question. How about those discoveries by female scientists? So, once again, we're checking to see if it is historically aware of the context of the conversation. In fact, maybe a better question would have been, are any of those discoveries by female scientists? We'll just see. Okay, so I think we can take that the OpenAI API is aware of the historical context, but I asked it a poor question there. Remember, you're only as good as your prompts. And I gave it a poor prompt there because I wasn't really referring to what was above. But I've got a answer back, and then once more, I can go in and start a new conversation. So there you have it. That is basically a start to finish tour of how you can create a ChatGPT clone using Bubble. How you can do it in 30 minutes and you can basically end up with a setup like this.
Now do check out our other videos. Subscribe to our channel. We release new videos on Bubble. We love Bubble. We believe that you can make incredible web apps with Bubble. You can start businesses with Bubble.
If you need any one to one help, you can head to our website planetnocode.com. You can sign up for a coaching session with me and I will help you one to one, troubleshoot, discuss, build Your App. If you want access to even more videos than you can find on our YouTube channel, you can head to our website where we have some member only videos. You can find out all about our membership there. If you have any requests for video topics, you can do so by leaving a comment or by getting in contact with us through our website.