DEV Community

Martin Niombela
Martin Niombela

Posted on • Edited on

OpenAI and React Native: JARVIS project v.1

Yo devs!

I've been writing articles on my own for a while now, and I thought it might be interesting to post them here too. To do that, I'm starting by reposting an article I wrote a year ago on my own blog (in 2023), so feel free to react in the comments, so I'll know if I should do any more!


As I write this article, the world is on fire with AI. ChatGPT, Microsoft Bing, even Bard, all these AIs are the talk of the town, for good or bad (with a special mention for the rocky beginnings of the AI version of Microsoft Bing). And while the devfluencers (yes, they do exist) are talking about the arrival of OpenAI's GPT-4 model, I myself wanted to try out what could be done with one of the models offered by OpenAI.

Between image generators (Midjourney, Stable Diffusion), music generators (Mubert), and less... "serious" AIs (a nod to ChatCGT, a very French initiative), there are projects to suit every need. But to access these AIs, you have to open your web browser and share the AI's resources with the thousands of other requests sent at the same time as yours. So I thought I'd make my own chatbot! And who knows, maybe one day I'll turn it into a sort of Jarvis, like Tony Stark's?

For my personal client, the choice of a mobile application seemed obvious. After all, it's the future Jarvis, isn't it?
So I chose to work with React Native, as I was already keen to find out more about this framework. And, to keep with the AI theme, I chose ChatGPT to help me get started on this project. Although ChatGPT won't be doing all the code for the application, it might be interesting to have him as an assistant on this project, for which I haven't (yet) mastered all the subtleties of the technology used.

SPOILER ALERT 🔊 : This article could potentially be a little long. What's more, the application could only be tested on an Android device.

Setting up the project

After a quick request to ChatGPT about how I should go about making a chatbot app using React Native, I got an eight-step plan, complete with command lines and sample code. We start with the setup steps.

Environment

Two elements are essential for this project :

  • Node.js
  • Expo Go (A mobile app for testing a React Native project developed with Expo)

Once the installations are complete, you can create your project and see React Native's "Hello World" by running the following commands :

npx create-expo-app myChatBot && cd myChatBot
npx expo start
Enter fullscreen mode Exit fullscreen mode

Settings

Now that the project is up and running, there are two more elements to be recovered :

  • An OpenAI API key (to be able to communicate with a GPT-3 instance)
  • An icon for the application

The API key is required for each request made to the AI. You can obtain this key by creating an OpenAI account, and then managing the API keys for that account. The icon, meanwhile, will be used to display the application on the phone, as well as for a possible splash screen. If you're not so good at graphics, you can use a library of royalty-free images and the Figma template provided in the Expo documentation. For my part, I used the Reshot site to find my brain icon, which I reworked slightly using Inkscape.

cerveau.png

The images that will be used for the application's icon and splash screen must be added to the application's assets folder. You can then modify certain parameters of the application, such as the background colour of the splash screen, or that of the icon (in the case of an Android device).

"expo": {
  ...,
  "splash": {
    "image": "./assets/splash.png",
    "backgroundColor": "#333"
  },
  "android": {
    "adaptiveIcon": {
      "foregroundImage": "./assets/adaptive-icon.png",
      "backgroundColor": "#333"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Quick planning

At this stage, everything is running smoothly: the application has an icon, a splash screen and a simple "Hello World" screen. While ChatGPT has already provided me with sample code for my application's interface, I've chosen to add a few elements, resulting in the following list of elements :

  1. A little style
  2. A header (with the application icon inside)
  3. A list box for messages
  4. An input area with a send button
  5. A modal window for application information (version, name of developer, etc.)
  6. A spinner (while the AI thinks about its response)

Code

With my list and ChatGPT's indications, I already know where to start: setting up the first item on my list: the style.

So now it's time to tackle the App.js file!

A little style

In terms of style, I've chosen a 'Matrix' colour theme (green and black, in short). The header and the part containing the input area will be dark black, while the part displaying the messages will have a background colour closer to grey.

To distinguish between messages from the AI and those from the user, I've decided to give them different colours, as well as a different font. The application's loading spinner will also adopt the colours of the "Matrix" theme. To make it clearly visible, I've chosen to make it a relatively light green, which contrasts well with the black of the header and input area.

(The code corresponding to the style can be found in my App.js file, it might be a bit long to put it all here)

Interface

Although I had planned the style I wanted to apply to my interface from the outset, it was by actually working on the interface that I was able to "fine-tune" the styling details (border colours, spacing, etc.).

So, following my list and deviating from the model suggested by ChatGPT, I started by adding my header, which also includes the application's logo.

<View style={styles.container}>
  <View style={styles.topBar}>
    <View>
      <Text style={styles.itemTopBar}>My ChatBot v.1.0</Text>
    </View>

    <View style={styles.imageTopBarContainer}>
      <Image source={require('./assets/icon.png')} style={styles.imageTopBar}/>
    </View>
  </View>

  ...
</View>
Enter fullscreen mode Exit fullscreen mode

I then added the area for listing sent and received messages (using the FlatList element), as well as the last part of my interface: input area.

<FlatList
  style={styles.list}
  data={messages}
  renderItem={renderItem}
  keyExtractor={(item) => item.id.toString()}
  ref={refFlatList}/>

<View style={styles.inputContainer}>
  <TextInput
    multiline
    cursorColor="#00FF41"
    style={styles.input}
    placeholder="Votre message..."
    placeholderTextColor="#777"
    value={inputValue}
    onChangeText={setInputValue} />

  <TouchableOpacity style={styles.button} onPress={() => { handleSend(); }}>
    <Image source={require('./assets/send.png')}/>
  </TouchableOpacity>
</View>
Enter fullscreen mode Exit fullscreen mode

The interface is extremely simplistic, so all the elements needed to make the application work are already there. All that's missing is message management with the bot !

Messages

To "manage" messages, you first need to start by adding what you're sending to the list of messages, which is directly linked to the FlatList element in the interface. To do this, we'll use what React Native calls a state, a special kind of variable which remains between each reload of the display of an interface element.

Here, we use the useState() hook to get the state variable and its setter :

const [messages, setMessages] = useState([]);
Enter fullscreen mode Exit fullscreen mode

We initialise the list of messages as an empty array, which we then fill using the setter setMessages() method. Two methods are used to display the message :

  • renderItem() (inserts the component which represents the message into the FlatList)
  • handleSend() (adds the message to the state messages)

My request to ChatGPT has already given me the renderItem() method, which I then modify to add my CSS classes :

const renderItem = ({ item }) => (
  <View style={item.sender === "bot" ? styles.botMessageContainer : styles.myMessageContainer}>
    <Text style={item.sender === "bot" ? styles.botMessage : styles.myMessage}>{ item.text }</Text>
  </View>
);
Enter fullscreen mode Exit fullscreen mode

I then retrieve the handleSend() method given to me by ChatGPT, which will be activated each time the "Send" button is pressed :

const handleSend = () => {
  if (inputValue) {
    prompt = inputValue.trim();
    setInputValue("");
    setMessages([...messages, { id: messages.length + 1, text: prompt, sender: (messages.length + 1) % 2 == 0 ? "user" : "bot" }]);
  }
};
Enter fullscreen mode Exit fullscreen mode

At this stage, I can start testing the application via Expo and send messages within the application to adjust the CSS of the message bubbles.

bulles.png

Now that the list exists and the CSS is correct, we can try to have a real conversation with our GPT-3 instance. This is the most important part of our application, and it was at this point that I encountered the first real errors from ChatGPT in my initial request, not to mention the few inaccuracies I'd already encountered in its response, but which I was able to correct immediately.

ChatGPT's imperfections

I encountered my first problem with ChatGPT when implementing the dialogue with the GPT-3 instance. I encountered two blocking errors when trying to follow ChatGPT's instructions :

  • Using OpenAI with React Native
  • Accessing environment variables in a React Native application

In the first case, after querying ChatGPT several times to no avail, I decided to look directly in the OpenAI documentation (the doc never gives you away!), which gave me the lines of code I needed.

In the second case, I also made several requests to ChatGPT, all to no avail. This time, there wasn't really any documentation available, but I was able to find the solution to my problem thanks to a Medium article.

As for the reason for these erroneous responses, part of the answer to this question lies, I think, in two facts:

  • ChatGPT data only goes up to 2021
  • ChatGPT is not connected to the Internet.

From these two facts, we can see that there's a good chance that he's basing his thoughts and answers on deprecated documentation, which is problematic in the case of a framework that continues to evolve. What's more, it's impossible for him to correct himself by checking other sources on the Internet, since it only works with the data that was used to train him.

In short, as many people have already pointed out, ChatGPT is not yet ready to replace a human developer (well, that's true for GPT-3).

Completing the application

Messages, part 2

After successfully setting up communication with the GPT-3 instance, a problem arose: the messages were only displayed once the bot had responded. I had noticed that it was impossible to make a double call to the setter of a state work, so I looked to see if it was possible to do this in some way. Here, no request to ChatGPT, a standard Google search led me to a developer's blog post that contained the solution to my problem (and a detailed explanation of the problem's origin).

This changes the nature of what I send to the setter setMessages() in my handleSend() method :

// How to send messages "one by one"
setMessages(previousMessages => ([...previousMessages, { id: previousMessages.length + 1, text: prompt, sender: "user" }]));
Enter fullscreen mode Exit fullscreen mode

A little style, part 2

In terms of the application's style, I wanted to emphasise the difference between the bot's messages and those of the user. So I chose to change the font of the bot's messages to monospace (a bit of a cliché, but it's in the spirit of the application, after all).

I also wanted to adjust the spacing between the FlatList and the rest of the interface elements. At first I'd opted for padding but, after several attempts, it still wasn't what I wanted. While doing some research (again without the help of ChatGPT), I came across a issue Github on the React Native project repository.
Finally, the solution was simple: change the padding to margin.

Additions and embellishments

A modal, an application

Most applications have an 'About' section, dedicated to displaying various information about the application or the person who produced it. So I wanted to add this to my application!

To do this, I used the Modal element in React Native, which I bring up by pressing on the application icon in the header. In this modal, the information appears in the form of a bulleted list, which I display via their Unicode code (I found the trick on this article on the atomlab.dev website). For some of this information, I wanted to integrate a hypertext link, so I used the Linking.openURL() method to achieve this.

As I wanted the modal to be displayed above the FlatList of messages, I also had to add an overlay, and make a few changes to the CSS (particularly to the z-index of the elements).

modal.png

Loading spinner, for feedback

As its name suggests, a loading spinner is an element designed to indicate a loading state. It's therefore necessary to have one, so that the user knows that the bot is going to respond, and that it's not just the application that's crashing!

As this is a common feature these days, when it came to adding this element to my application, I preferred to follow an old adage from developers that says:

Don't reinvent the wheel.

So I chose a simple spinner whose style is easy to adapt: react-native-loading-spinner-overlay.

Auto scroll to last message

For this first version of my chatbot, something trivial yet very common in messaging applications was missing: scrolling to the last message. In most applications, when a new message appears in the list, the view moves to display the message in its entirety (or at least to display the end of the message). However, when I tried to add this "harmless" behaviour, I came up against a thorny problem: an 'index out of range' problem (classic, but not always obvious).

After a few quick searches with no conclusive results, I decided to turn once again to ChatGPT. Armed with my new understanding of how a state and its setter work, I explained my hypothesis as to why my scroll wasn't working, and asked it, on the basis of this hypothesis, how it would solve the problem.

And I have to admit two things :

  • My hypothesis was correct
  • ChatGPT very quickly understood the problem and provided me with a suitable solution

This solution boils down to executing my scroll at the right moment, i.e. at the end of the animation showing the message in the FlatList. This gives a function such as :

requestAnimationFrame( () => refFlatList.current.scrollToEnd({ animated: true }) );
Enter fullscreen mode Exit fullscreen mode

I don't think I would have found this method so quickly, without the help of ChatGPT, and that's one of the advantages that can't be denied: it saves a lot of time.

Build v.1.0

Satisfied with the elements added so far, I thought it was time to install version 1 of this project !

To build my project, I used the Expo documentation as a guide to set the various parameters required for the process to run smoothly. I also noticed something : the environment variables as I was using them were not embedded in the APK produced by the build. As a result, there was no API key, and therefore no bot !

To remedy this, I had to create a secret with an EAS command (the tool that manages Expo builds) and modify the code slightly. And there you have it, a first version !

Conclusion

This little project has no other purpose than to allow me to work on React Native, and incidentally to pretend I'm Tony Stark. It's still very basic, but I'll probably come back to it later to add other features that will bring my chatbot closer to the level of the famous Jarvis.

All the code is available on my GitHub repository. You can find the article on my blog and you can also follow me on twitter, where I mainly talk about tech and my dev activity.

Until next time! 👋

Top comments (0)