ChatGPT Clone with Next.js, Supabase and Jotai

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in today's video we are going to build a chat GPT clone with Super Bass and next gs30 our chat GPT clone has almost every feature that the real chat GPT has you can start your channel by asking questions immediately like this hello word as you can see it's streaming the response you can regenerate response by clicking this button also you can change your current chat settings like this if you want to use another model like gpt4 you can choose from this select but remember that it's almost 10x expensive than the previous model also you can provide your own system prom when you ask the chat GPT its name like this what's your name and it will say the maker AI because we have provided this system prompt you can change this to try another prom like this and you can regenerate your response as you can see now it says I am fake layer right yeah cool you can create new chat by clicking this button and you can start your chat with the same input also the app has authentication system you need to log in your account to reach the app to start chat to use root handles and finally our whole app is mobile responsive like this you can use this app on your mobile devices as well in our app you can change the team we have dark team and the light team like this in the near future I will add prompts Library you can select you can choose predefined prompts from the library or you can save your responses into a collection for example you you have created a blog post you can save this blog post into your collections into your database and finally we will create plugins we will create GPT agents for this app for example you can get real-time data with Google search or Etc our whole data stored in super based database for example if you reload this page as you can see we are getting whole data back from the database if you are ready let's get started [Music] okay let's check how we structure our for the back end we will use Super Bass for authentication authorization and database but for the sake of this course we are not gonna dive into authentication part because I have already created a video that covers this topic you can find this video from the description I'll put the link into description okay let's check our super based dashboard sorry yeah here to see what we have created as tables and columns you can see the profiles table and you can learn how to create this table based on our users from the video that I mentioned earlier for this app we will create chats and messages tables in chats table we will hold our chats in this table we have a column for ID this is primary key and they created that title the title of chat and the owner this is a relational column based on the profile table the model system prompt and advanced settings so we can get those data from Super Bass and put this data into our state okay cool now we have messages table again the ID and created that column and the content for the response open air response and the role for the open arrows for example it can be user or assistant user for user message and assistant for open air responses and the chat ID again this is a relational relational column owner this is the same column as in chats okay cool So based on this owner columns we will create row level security which means that only users can fetch their data if they are the owner of chat or message to create this role level security policy you can go to authentication part and the policy section you can create new policy with this button let me show you what I what I created I enable all access which means users can select delete update or insert data with this policy if they are authenticated and if their uid equals the owner as you may remember we have created a column called owner which is a relational column with profiles so when we are adding messages or chats we need to put this column into our table and with that column we will make sure that only owners can fetch their data or insert or update or delete their data okay cool now we are done with Super Bass we can go our app and check our app structure okay for the front-end part we will use nextges 13 with the new app directory and rootenders as well as streaming in the new experimental Edge runtime in rule tenders also we have some important package that I want to mention like jotai for State Management we are going to match manage whole state with jotai and we will build our application logic inside jotai also we will use SWR for client-side client-side data fetching we will use and Radix UI for UI library with Tailwind CSS we will use react markdown for rendering openers responses also we will use highlight.js for rendering code blocks in the open air responses okay cool we can start with checking our application our folder structured in our app directory we have two folders called Chen and Logan in login we have a simple page the login page and we have a login form if user is not logged in we redirect user to the login page like this if user tried to go home page it will be he will be or she will be redirected to the login page we create this logic inside of the middleware we reach to the session with Super Bass and again if you don't know how to do that you can check my previous video that I cover this topic we reach the session object in this middleware and if there is no session and the path name starts with chat we will redirect user to the login page or if there is a session and the path name equals the home page we redirected to chat page because we don't use this home page in our application we have only chat or login page in our home page we simply redirect user to the chat as you can see if there is no session and if you use it goes to the home page he will be redirected to the chat and after this redirection he will be redirected to the login page okay cool and if this session he will be redirected to the chat cool now we can log into our account to see if it works yeah cool it works in our chat page we have a page for this chat room like this and also we have a dynamic rune based on chat ID like this if we click this button as you can see we we go to this rule this is a dynamic rule based on the chat ID and if there is Chad we can see the chat content to make this happen we need to fetch this chat in the server component and return this chat if it exists and if it doesn't exist we simply return not found fun function this is a helper function from next navigation and the user will be redirected to the not fund page we can try this like this we can simply remove a letter from ID as you can see there is no page like this okay cool now we can go to our chat page again cool also we we fetch our data in the server component and pass this data as prompt to our checkbox as well as initial messages the current chat based on the ID and current chat messages like this okay cool so before dive into the more complex complicated part we can check our UI first because I want to show you how I created this sidebar as well as the this is the input part then we can dive into the business logic and the state management and the rule tenders okay cool now we can check our layouts first to understand how is structure our app in the root layout we have our providers the Super Bass provided to use a single Super Bass instance in our app or Superman's old provider to to manage our user and the authentication logic and the team provider for this change team functionality and lastly we have at your type provider for our State Management Library normally you don't have to use provider especially in the single page applications that you build with react but in SSR applications you need to wrap your application with app provider and if you want to learn more about jotai you can check my previous videos as well but we will dive into some part of Joe Tai in this video when we come to business logic part okay cool now we can check our chat layout chat page layout in in this layout we have a sidebar overlay this is a overlay in Mobile's sidebar when we click this button I mean when we click this overlay we can close this sidebar on mobile and also we have this sidebar these are the same sidebars in desktop and mobile we can check our sidebar first in our sidebar we have and this is a stateful component which means client component the most important part in this component is this custom hook called use chats because we manage our chats logic in this customer as you can see we return at chat Handler from this hog and with this Handler we control adding new chat functionality when we click this button as you can see we are adding a new chat so to understand the logic let's check this customer as you can see we import bunch of stuff in this hook for the old and super based part we have two separate custom hooks but we are not going to cover this part in this video and the States right we will use jotai to manage our state and we will dive into this part in the feature right now you can you can think this Parts as regular use States we simply hold our open AI settings in the state and chats and sections okay cool in here we have a simple fetcher with this Factory fetch our chats and the messages come like this and we need to get our user ID since we have a row level security right because only owners can fetch their data that's why we need to pass our user ID into this function and we need to be authenticated okay cool if we have data we simply return this data and we use use SWR for this client-side data fetching because we need to manage our ease loading State and the mutations as well as cash because with this use SWR we automatically cache our data for example if we if we fetch or me when we change the page as you can see we we don't refetch again because we have already fetch this data and cache this data that's why we don't need to fetch it again and again right okay cool now we have this add new chat Handler as I showed you in this sidebar component this is where we manage our add new chat functionality and for this purpose we need to reach our settings right our model and the system prompt and the advanced settings because as you may remember in our chats table we have model system prom and the advanced settings right yeah and we simply put new conversation for the title and we mutate our state when we create new chat because we need to list this new chat in this list right like this cool and finally if we are succeed we we need to push the user to the new chat page right the chat and the ID as you can see here like this and we we put the search param into URL because we need to know if it's a new chat or not I'm gonna show you where we use this search param later okay cool now if we have the data which means chat this data this checklist we need to set this data into our state this set with this setup function called set chats remember we import this state from jotai where we manage our state okay cool and finally we return all these chats is loading State error mutate and the add chat Handler cool let's get back to our sidebar component if there is anything missing we have a simple State and the setter for mobile menu to control the state of this sidebar also we have a profile menu in the footer where we can control our team like this as well as logout function but these are a bunch of basic stuff you can check from this source code later right I'm not gonna dive into this part okay cool now we can close this tabs okay cool and we can go to our layout to see what is there and after sidebar we have children and in this page as you can see we have a component called chat box as children right we fetch our data in server and pass this component as props like this okay let's check this chat box component in this chat box component we have another custom hook called use check this time we are controlling the logic we are managing the logic of the current chat the single check right not whole chats the current chat like this in this page okay cool let's check what we have this in this custom hook and before going to discuss the mooc as you can see we pass our initial messages and our current chat that we get from our server component okay cool now we can go to our use chat custom Hulk in this custom hook we have a bunch of states and the Setters but we are going to dive into this part later this section but first we need to set our initial messages and our current chat that we get from props right we fetch this data on the server pass this custom hook and now we need to set this data into our state our message State and our current Chat State right and if you remember there was a search param called new right if it's if it exists we know that it's a new chat and if it's a new chat we need to send our initial message let me show you if we go to our chat page like this this is the page where we redirect user after they log in we can send our first message hello world again and if we send this message the user will be redirected to the this URL with this search param and if there is a search param called new we know that we need to send initial message to get response right that's why we need to use our add message Handler and after we send our message we need to delete this param and also we need to replace this URL with this one which means that we need to delete the search bar okay cool and finally we return this state hasht messages because if the chat has messages we don't want to show this pop-up we want to show messages right that's why we need to know if there's a message or not okay cool now we can go to our atoms to see how we manage our state and our logic okay cool now let's go to our atoms folder and in the atoms folder we have a file called chat.ts where we keep our whole atoms related this chat feature okay let me make this screen bigger and close this sidebar okay cool in this file we have a default system system prompt and we have open AI settings atom where we keep but we manage the open AI settings for new chat in this page this chat page let me change this model we simply change this state model system prompt and advanced settings okay cool and the another thing is open AI payload to combine all settings and messages in a state we use this read-only atom we simply get data from other atoms to understand this atom before we need to understand the other atoms like current chat atom messages atom and Etc okay let's keep continue this is a simple atom to control the handling state when we send message we are changing this state if it's handling you can see here write me a basic react component when open AI is responding we set this handling atom to true and if it's true we can show this stop button like this stop generating if you click this button it will stop and if it's not handling we can show this regenerate response button right okay cool this is a ref that we create with create rap to control scrolling in this chat box because as you can see if we are streaming we need to scroll down as we let me create another message okay cool [Music] let's create another one for form four validation formulation like this as you can see as we goes down we scroll down as well that's why we need to have this ref in our state right okay cool this is a simple atom to control the input value and this is a chat ID Adam it's a read-only Adam because we use this ID in everywhere that's why if we want to get this atom I didn't want to use this current chat atom every time so this is a read-only Adam when the current chat atom change the ID change as well and we can simply get this chat ID from this atom okay and the current chat atom with this atom we hold the current chat data if there is a current chart this is a chat with message count and settings if there is no chat it's set no and the chats item where we keep our chats and the messages Adam that we keep our messages okay and the current chat has messages Adam as you may remember we were controlling this state to show this pop-up or the messages and this abort controller atom if you remember when we are streaming response we have option to stop generating right that's why we need to control our fetch to control our fetch we need to create a board controller with this controller we can cancel or fetch that may cancel our fetch also we need to set handling atom and also we need to set another about controller to manage our fetch okay cool and we can write this logic in this cancel Handler atom with this atom we can simply return if it's handling and this Setter function to set our about controller to abor and these states okay the most complicated part is the add message atom in this atom we have a state the handling State we return this state from this atom and also we return a set of function in this set of function we have two kind of actions called generate or regenerate if users send regular message we use this generate action if user wants to regenerate response we use this regenerate action in this function we first need to get our input value and is handling State and the chat ID if it's already handling we need to return or if there is no input value with action generate because if user wants to regenerate response user already sent this message and there's nothing in this input right okay cool and after this early return we can build our user message object we need to create this object in this function scope because we will use this object in uh in inside some if as block and this is add message to Super Bass Handler as you know we we are using super basis database right we need to send our messages to the database because if for example if we refresh this page as you can see we get our messages from the database that's why when we create message we need to push this message into our state but also we need to push this message into the database that's why we need a Super Bass Handler okay cool now we can start handling if the action is generate we know that user are Sending message right that's why we need to add the user's message to the state and also we need to clear the input right okay cool now we need to send this to the API but first we need to create our initial message because our API will stream our stream the response but it takes time right we need to set this initial message to show the thinking State as you can see hi my name is Baton like this it says thinking to show this AI message before we get response from the open AI we need to set initial message and we need to um set an initial ID for this message we can create this ID with this uuid V4 function that we get from this uuid package okay cool now we can send or payload to the R root open AI chat to S to get our payload if you remember we have already created and Adam open AI payload in this open AI payload Adam we simply get our current Chat State and we get our model from this state and also our system prompt and our messages like this as payload you need to send your model your messages and your advanced settings like this model system prompt and advanced settings and we put this system prone with our messages as we send message like this do you remember my name as you can see it remembers my name because we are sending the whole chat history to the open AI from the state in the near future I will create another method because in open AI as you can um as you may know there is a token limit the token limit is like 8 000 or 4 000 I don't remember the exact number at the moment but as your chat history grows you can you can you can go beyond these limits right so you need to create another logic for this purpose and thanks to Super Bass we can create this logic but it's it's a topic of another video okay cool now we send our payload to the our room and also we need to set our signal into this fetch because we need to control this fetch with this button right if you click this button we simply set our a board controller Adam where was this the atom yeah we simply change the state of this cancel Handler atom right okay cool now we can wait for the response if it's not okay we can draw an error and if there's a data this is a readable stream I will I will show how we can create this kind of functionality with open a open AI and the new root Handler in Nexus 13. if there is a stream we can read this stream and we simply create a variable like this done equals false because we are still getting response from our fetch right it's streaming at the moment and if it's not done we need to set our messages with these chunks right in every time it trims another chunk like this please create a simple blog post for react State Management foreign chunks from the open air right as we get those chunks we need to set into our state and if it's done we need to oops it's still creating it's amazing right okay if we still get a response we need to set this response as these chunks into our stay okay and as we get the responses as we set those chunks we need to scroll down right that's why we need to get our chat box ref and if there's a chat box ref we need to scroll down and finally we need to stop handling and we need to add those messages to the super base we send this message and also we get this response right we need to set those messages into Super Bass like this first we need to understand if there's a user message or if it's only AI message I mean if we regenerate response we simply delete this message but we keep these user use this message right that's why we need to we don't need to send this message to the Super Bass again because we have already this message in Super Bass But if we regenerate the response we we don't have this message anymore that's why we need to set the new response to the Super Bass that's why first we need to understand if it's a regenerate or generate when we understand this we need to change our dummy ideas that we created with the uuid package because in our database we have our real IDs right that's why we need to change the dummy IDs with the real ones for this purpose we we will need to create a for Loop and in this Loom check and find our messages and set our IDs into these messages okay cool and in this part this is the regenerate part in this part there is only the AI message because we are regenerating the response and lastly in our application you can see that if we send messages for example we can say that could you please tell me a little joke about American politics let me get the message we said our chat or conversation name like this American politics joke request to get this title we also use another rule called API slash open Ai and chat title and if it's a new chat which means that if the message length equals 2 we need to get this title right for this purpose we can use our add message Handler because then we send a new message and if there is only two messages we can run this logic to run this logic we simply send our messages and the check ID to the root I will dive into the roots after this section and also we need to set our title that we get from the response okay the final function is the regenerate Handler atom in this function we have a state in this state we simply check if we can regenerate or not if to if we want to make this regenerate balance active we need to check if there is an assistant message for example in our chat page I'm sorry chat page there is no button right because there is no AI response at the moment which means that we cannot regenerate any message and also if we are not handling oops sorry if you are already handling which means that if we are already getting responses from our route from the open AI we cannot set we cannot regenerate response let me show you send okay cool Place some rise this post like this it's already responding to our message that's why we cannot show this regenerate response button cool if these states are true the both of them we can simply return this Boolean and in with this regenerate 100 atom we can get this state right and also we have a Setter function in this function we remove last assistant message because we want to regenerate right like this first we need to remove it and the last message has a role of assistant we need to set this new state into messages Adam and we need to send sorry we need to remove this message from our database as well and finally we need to send this new message with ADD message Handler and we need to set its action to regenerate right because we are not generating based on our input value we are regenerating and user already send the message right okay cool I can it might be complicated for you I mean the your type part if the jotai part is complicated for you you can check my previous videos I already explained the mental model behind this Joe Thai State Management Library as you can see it's it's it has a kind of uh Atomic approach you can combine atoms together and you can get the States in global environment you don't have to pass those States from parent components you can get this state in every component because it's Global State like this you can get this stain in our chat box component and also we can get this stain in another components as well okay cool now we can go to ranging part and the rootendness okay let's check what we have inside our rootendness in our API folder we have two folders called open Ai and Super Bass in Super Bass folder we have a root called message and in this room we simply insert message first we need to check if there is a message in our body if there is no message we return no message response and we need to create our supervised client to check if there is a session which means that if user is authenticated if there's no session we need to return not authorized response if there is user we need to insert this message that we get from our body and we can insert these messages like this and if we successfully insert this message we can return the inserted message cool and the second one is the patron currently there is a bug with the delete request in next year's [Music] 13.3.0 that's why I use patch but in the future we can use delete in this rule we simply delete the message from our database because sometimes we regenerate the response and if we regenerate response we need to delete this message from our database right with this root we can make this happen okay cool and in open AI folder in opener room we have two separate routes called chat and chat title in this chat title rune as you can remember we need to set the conversation title based on our message right again we check our session if user if there is no user there is no session we return not authorized response and if there is user we need to get our conversation title and for this purpose we set a system Pro System prompt like this based on the previous conversation which means that we have the to message from the new chat like this hello my name is Martin can you help me to create a react component as you can see it sets are title based on these two methods so we need to send those messages to the open Ai and we need to set our system prompt like this and we we should say return only the title in this function we use gpt4 model because gpt4 model follows this system prompts in a in a more strict way because with the previous model sometimes it doesn't follow the rules which we declared in this system prompt but you can try with the previous model if you change your system prompt you can try it maybe you can get a better response with the previous model and if we have this response we can send this response with our next response but before sending this response we need to set our title that's why we have provided our chat ID with are messages we can set our title with this function okay cool now the chat rule that we use to generate our responses again if there is no session we return not authorized if there is no payload we return no payload and if we have more of them we can simply call this function so let's check this function and learn what it does in this open AI stream function we simply fetch this URL API dot openai.com V1 chat slash completions and to call this API run we need to set our open AI API key in our environment variables you need to set your open air key and you need to set your Super Bass URL and the public key okay cool now with this new readable stream function we can create our stream and in this function actually we simply say to the open AI let's stream your response as chunks rather than sending the whole response when it's done because open AI is creating this response messages as chunks and we can get those chunks as the open air created right and in this function we get these chunks and return this stream as result and in our root we stream this response but to make this happen we need to use this runtime The Edge runtime this is much more cost effective and much more faster than the regular runtime the node.js runtime okay cool now we have done with our rootenders so we can check our render function let's check our components folder in our components folder we have a folder called chat and in this folder we have a file a component called message and in this component we render our messages if it's a assistant message we render this Avatar or if it's a user's message we render this profile image and for our message we use this react marked on Library with this Library we can render markdown as jsx elements as you may remember we we have a system from in our atoms let me show you okay in here open AI payload atom we say always answer in markdown to the open AI That's why we are getting marked on content from the response right so we can render this marked on as jsx with this react Mark tone Library in this Library we can create controllers like this if it's a code block we can create a custom Logic for this block if it's a code block and if it's inline code like this let me show you create a react component for login with input state foreign this is a code block and these are inline chords for inline codes we simply return this styling and for the code block we return this because the children is our message and for our message we can use highlight.js with highlight JS we can style this code block in a way that we want for example in this application I use highlight.js with GitHub dark CSS Style foreign create a logic for copying this chord for example I copy this Con and I can paste into our free escort right like this okay cool to to create this logic we need to pass a ref into our code block because we need to copy the content inside of this code block right for that purpose we can create a reference with use ref hook and we can pass this graph into our Quadra code block like this and when we click this button that we created with another Library called copy2 clipboard you can install this Library from Riyadh copy to clip copy to clipboard and as text probe we can provide with our ref code ref dot current dot inner text as string and when we click this button we simply this code block okay cool I think now we are done let me check if there is anything that I Miss yeah it looks like we are done Okay cool so what's next I will create another features that I mentioned earlier like prompts Library Collections and plugins and also I will create another loading for holding our chat history in super basis Vector database which which means that we can hold our chat history in in a vector database and we can recall this data when we need and we can pass this data with a chat history with this approach we can create Global memory for our chat application for example if if we recall all the message that we have we can we can ensure that GPT can remember everything in our every chats right as a next video I will create this Logic for this app and after that we will create this features prompts Library Collections and plugins with the GPT agents thank you for watching guys and if you are curious about this curious about the feature of this app please make sure to subscribe my channel and also you can put a star into my GitHub repo
Info
Channel: makrdev
Views: 3,357
Rating: undefined out of 5
Keywords:
Id: yrXLvCB0ByA
Channel Id: undefined
Length: 70min 23sec (4223 seconds)
Published: Tue Apr 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.