Building a Fitness and diet Chatbot with FaunaDB, OpenAI, and Twilio in Python
In this article, you will learn how to build a Fitness and diet chatbot using Python, FaunaDB, OpenAI, and Twilio. The chatbot will be able to communicate with you via WhatsApp and provide responses based on your conversation history. You'll be walked through the code and understand the key components and libraries involved in the process.
Prerequisites
Before you begin, make sure you have the following:
Python 3.x installed on your machine
FaunaDB account and API key
OpenAI API key to access the
gpt-3.5-turbo
modelTwilio account SID, auth token, and a phone number
Ngrok: It allows us to expose our local Flask server to the internet.
Setting Up the Fauna database
To begin using Fauna, your initial step is to establish an account on the official website. This can be accomplished by providing your email address or utilizing your GitHub or Netlify account through the following link: Fauna. For efficient communication with the chatbot, it is necessary to utilize the Fauna database for storing and retrieving user messages.
Creating the database
After creating your account with fauna, you will be creating a database to store the Users
and Messages
. Here you'd be asked for the database name and you are going to name it MyChatBot
and the region is going to be set to Classic
and like that you've created your database. Then, you should be presented with a screen like this one below:
Creating your collection
Next, you will be creating 2 collections, which is basically Tables
in the SQL world but with a twist in our context.
To create your Collection, click on the Create Collection
button on the home page and give it a name, but since you'd be creating the two collections will be naming them Users
and Messages
. The "Users" collection is for storing the user's username from WhatsApp, while the Messages
collection is for storing the user's chat history with the bot. You will be asked for History Days
and TTL
. The History Days is used to define the number of days Fauna should retain a historical record of any data in that particular collection while the TTL serves as an expiry date for data in the collection. For example, if the TTL is set to 7, any data stored in that collection will be automatically deleted 7 days after its last modified date, but for this tutorial you'll not be needing it so it will be left untouched. After creating the two collections, you should be seeing this:
Creating the Index
Wondering what an Index is?, Well an Index is simply a way to browse data in a collection more efficiently by organizing it based on specific fields or criteria, allowing for faster and targeted retrieval of information. To create your Index, you'll navigate to the Index tab and you should see something like this:
To create the Index, you first of all need to Select a Collection
, then specify the Terms
, which is the specific data the Index is only allowed to browse. But for this you will be creating two Indexes, users_by_username
which would be under the Users
collection for registering users and users_message_by_username
which would be filtering the user's messages by their username. The Terms
for users_by_username
and users_messages_by_username
would be set to data.username
will be set to data.username
, then click SAVE
to continue.
Getting our Database key
Before you proceed to building the ChatBot, you need to create an API key that would allow your application to easily communicate with your database. To create an API key, you need to navigate to the security tab on the Fauna sidebar (on the left side of the screen).
Next, you will click on the NEW KEY
button that will navigate you to the page below:
Here, your key role would be set to Server
instead of Admin
and set your Key name to our database name which is optional, then click on SAVE
and you'd be navigated to a page where your database key would be displayed and meant to be copied immediately. You should see something like this:
After getting the API KEY
, store it in the .env file in your coding environment.
Configuring Twilio
In order to demonstrate the procedure, you will set up your Twilio account to use WhatsApp by making use of the Twilio Sandbox for WhatsApp. To access the WhatsApp Sandbox in your Twilio Console, navigate to the Messaging section located on the left sidebar (if you can't see it, click on Explore Products to unveil the product list, where you can locate Messaging). Afterward, expand the "Try it out" dropdown and choose "Send a WhatsApp message" from the available choices. Subsequently, you will be presented with the following display:
After scanning the QR code, you will immediately receive a "join disease-see" message on your WhatsApp. Simply send the message, and just like that, you will be connected to your WhatsApp account.
Setting up the Environment
First, create an app.py file in your coding environment then you have to install the necessary libraries. You need to install faunadb
, openai, python-dotenv
, twilio
, and flask
libraries. You can use the following command to install them: pip install telebot faunadb openai python-dotenv twilio flask
Once the libraries are installed, you can proceed to the code implementation.
Importing Libraries
Let's start by importing the required libraries in your Python script:
import telebot
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient
import openai
from dotenv import load_dotenv
from twilio.rest import Client
import os
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
You need to import faunadb
for integrating FaunaDB, openai
for utilizing OpenAI's gpt-3.5-turbo
model, dotenv
for loading environment variables, twilio
for sending and receiving WhatsApp messages, os
for accessing environment variables, flask
for creating a web server, and twilio.twiml.messaging_response
for generating TwiML responses.
Initializing Flask App and FaunaDB Client
Let's initialize the Flask app and create a FaunaDB client:
app = Flask(__name__)
def prompt(username, question):
load_dotenv()
client = FaunaClient(secret=os.getenv('FAUNA_SECRET_KEY'))
# ...
You create a Flask app using the Flask(__name__)
constructor. Inside the prompt
function, you load the environment variables using load_dotenv()
and create a FaunaDB client using the provided secret key.
Storing User Messages in FaunaDB
Within the prompt
function, you store user messages in FaunaDB using the FaunaDB client:
data = {
"username": username,
"message": {
"role": "user",
"content": question
}
}
result = client.query(
q.create(
q.collection("Messages"),
{
"data": data
}
)
)
You create a dictionary data
containing the username
and the user's question
. Then you use the client.query
method to create a new document in the "Messages" collection, storing the data
dictionary.
Retrieving User Messages from FaunaDB
You retrieve the user messages from FaunaDB by querying the database based on the username
:
index_name = "users_message_by_username"
username = username
result = client.query(
q.map_(
lambda ref: q.get(ref),
q.paginate(q.match(q.index(index_name), username))
)
)
messages = []
for document in result['data']:
message = document['data']['message']
messages.append(message)
You specify the index_name
for the index to query from FaunaDB. Then you use q.paginate
and q.match
to retrieve all the documents associated with the given username
. You loop through the result and extract the message
content, storing it in the messages
list.
Setting up OpenAI API
Let's set up the OpenAI API and define the assistant's persona:
openai.api_key = os.getenv('OPENAI_API_KEY')
system_message = {"role":"system", "content" : "You are a dietitian, food nutritionist, and fitness consultant, you provide expert guidance and advice to individuals facing dietary challenges or seeking direction on their food choices and exercise routines. You offer personalized recommendations and solutions to those who are unsure about the right foods to eat or the appropriate exercises to engage in. If user says hello or any greeting introduce yourself."}
prompt_with_persona = [system_message] + [
{"role": "user", "content": message["content"]} if message["role"] == "user"
else {"role": "assistant", "content": message["content"]} for message in messages
]
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=prompt_with_persona
)
generated_reply = response["choices"][0]["message"]["content"]
You set the OpenAI API key using os.getenv('OPENAI_API_KEY')
from the .env file as created earlier. Then you define a system_message
dict representing the assistant's persona. You construct the conversation prompt by combining the system_message
with the user and assistant messages from the messages
list.
Afterward, you make an API call to the OpenAI ChatCompletion API using openai.ChatCompletion.create()
, passing the conversation prompt as the messages
parameter. The API response contains the generated reply from the model, which you extract and store in the generated_reply
variable.
Storing Assistant's Reply in FaunaDB
You store the assistant's reply in FaunaDB using the FaunaDB client:
newdata = {
"username": username,
"message": {
"role": "assistant",
"content": generated_reply
}
}
result = client.query(
q.create(
q.collection("Messages"),
{
"data": newdata
}
)
)
You create a new dictionary newdata
containing the username
and the assistant's generated_reply
. Then you use the client.query
method to create a new document in the "Messages" collection, storing the newdata
dictionary.
Handling WhatsApp Messages
Now, let's implement the route for handling WhatsApp messages:
@app.route('/whatsapp', methods=["POST", "GET"])
def chat():
load_dotenv()
twilio_phone_number = os.getenv('TWILIO_PHONE_NUMBER')
sender_phone_number = request.values.get('From', '')
username = request.values.get('ProfileName')
question = request.values.get('Body')
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
Twilioclient = Client(account_sid, auth_token)
client = FaunaClient(
secret=os.getenv('FAUNA_SECRET_KEY')
)
reply = None
user_exists = client.query(
q.exists(q.match(q.index("users_by_username"), username)))
if user_exists:
reply = prompt(username, question)
answer = Twilioclient.messages.create(
body=reply,
from_=twilio_phone_number,
to=sender_phone_number
)
else:
client.query(
q.create(
q.collection("Users"),
{
"data": {
"username": username
}
}
)
)
reply = prompt(username, question)
answer = Twilioclient.messages.create(
body=reply,
from_=twilio_phone_number,
to=sender_phone_number
)
return str(answer.sid)
Inside the /whatsapp
route, we load the environment variables, retrieve the necessary data from the incoming request, and create the Twilio and FaunaDB clients. We check if the user exists in FaunaDB using q.exists
and q.match
. If the user exists, we call the prompt
function to generate a reply and send the reply back to the user using Twilio's messages.create()
method. If the user doesn't exist, we create a new document in the "Users" collection and follow the same process as above.
Running the Flask App
Finally, let's run the Flask app:
if __name__ == '__main__':
app.run(debug=True)
We use the if __name__ == '__main__':
conditional statement to ensure that the app only runs if the script is executed directly, not imported as a module. The debug=True
is set to in order to enable debug mode for better error messages during development. Now run python
app.py
in your terminal, it will run on localhost:5000
. You can now go ahead to ngrok and run ngrok http 5000
.
Copy the circled link and paste it in your WhatsApp sandbox
There you have it, you now have your own personal WhatsApp fitness and diet bot
Conclusion
In this article, you learned how to build a chatbot using Python, FaunaDB, OpenAI, and Twilio. We covered the code step by step, from initializing the Flask app to handling WhatsApp messages and generating responses using OpenAI's ChatCompletion API. You can use this code as a starting point to develop your own chatbot with additional features and integrations.
Happy Building!!