Build this - Chattybot
Important Links
Introduction
In today’s article, I’m going to walk you through how to build your own chatbot using Python with Rasa for the backend and React for the frontend.
First things first, we need to make sure we have Python installed on our machine. I’ll be using Python 3.9 for this setup, so let’s download that version using Homebrew.
brew install [email protected]
Check the version check
python3.9 --version
Create Project structure
mkdir chattybot
cd chattybot
mkdir backend
cd backend
Creating a virtual environment
python3.9 -m venv rasa-env
To activate the environment, simply run this command:
source rasa-env/bin/activate
Now that we’re inside the virtual environment, let’s install Rasa itself!
pip install rasa
We’ll also install some additional dependencies that we’ll use later for handling HTTP requests and cross-origin resource sharing (CORS).
pip install flask flask-cors requests
Now that we have everything installed, let’s initialize our Rasa project. This is where the magic happens!
rasa init
Rasa will ask if we want to create the default directory structure and sample files. Let’s go ahead and select ‘yes’ for that."
Rasa will automatically train a model based on this initial setup. The model helps the chatbot understand user inputs and generate appropriate responses.
Creating More Intents
Now that we’ve set up the basic Rasa chatbot, it’s time to make it more intelligent by adding custom intents. Intents are essentially the user’s purpose or goal when they interact with the bot. Let’s add a few more to make our chatbot smarter.
Update nlu.yml
file looks like. I’ve already added several new intents. For example, a greet
intent that recognizes different ways users say hello, and an ask_programming_languages
intent that allows the user to ask about different programming languages.
version: "3.1"
nlu:
- intent: greet
examples: |
- hey
- hello
- hi
- hello there
- good morning
- good evening
- moin
- hey there
- let's go
- hey dude
- goodmorning
- goodevening
- good afternoon
- intent: ask_programming_languages
examples: |
- What programming languages do you know?
- Tell me about different programming languages.
- What is Python?
- What are the best programming languages for web development?
- Can you list some programming languages?
- intent: ask_web_development
examples: |
- What is web development?
- Tell me about front-end development.
- What is the difference between front-end and back-end?
- How does React work?
- What is the role of a web developer?
- intent: goodbye
examples: |
- cu
- goodbye
- see you later
- bye bye
Once we’ve defined our intents, we need to add appropriate responses in the domain.yml
file. This is where we define how the bot responds to different user inputs.
Add responses in domain.yml
responses:
utter_greet:
- text: "Hey! How are you?"
utter_programming_languages:
- text: "I know about various programming languages including Python, JavaScript, Java, C++, Ruby, and many more. Each has its own strengths and weaknesses."
utter_web_development:
- text: "Web development involves creating websites and applications for the internet. It can be divided into front-end (client-side) and back-end (server-side) development."
Finally, to make sure the chatbot behaves correctly, we’ll create stories. Stories are training examples of the conversation flow. They help Rasa understand how to respond based on user inputs.
[On-screen: Stories in stories.yml
]
stories:
- story: ask about programming languages
steps:
- intent: ask_programming_languages
- action: utter_programming_languages
- story: ask about web development
steps:
- intent: ask_web_development
- action: utter_web_development
With these new intents, responses, and stories, our chatbot is ready to handle more complex conversations. In the next section, we’ll connect this to our React frontend and test the entire flow.
Creating the Flask Backend
Create app.py
in backend folder.
Flask backend will serve as the bridge between Rasa and our frontend, along with handling user conversations and storing chat history in a MySQL database.
Let’s start by creating our app.py
file. First, we’ll set up Flask and enable CORS, which allows cross-origin requests from the frontend.
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # Enable CORS for all routes
Now, we need to integrate MySQL to store the conversation data. I’ve already set up a simple MySQL database with two tables: one for conversations and one for storing messages. You can adapt this based on your needs.
db_config = {
'user': 'root',
'password': 'password',
'host': 'localhost',
'database': 'chatbot'
}
To send the user’s message to the Rasa server, we’ll define the Rasa API URL and create a function that makes an HTTP POST request to Rasa.
RASA_SERVER_URL = "http://localhost:5005/webhooks/rest/webhook"
@app.route('/chat', methods=['POST'])
def chat():
data = request.json
print(f"Received data: {data}") # Log incoming data
user_message = data.get("message")
user_id = data.get("user_id")
conversation_id = data.get("conversation_id") # Conversation ID
if not user_message or not user_id or not conversation_id:
return jsonify({"error": "No message, user_id, or conversation_id provided"}), 400
print(f"Received message from user {user_id} in conversation {conversation_id}: {user_message}")
# Send message to Rasa
response = requests.post(
RASA_SERVER_URL,
json={"sender": user_id, "message": user_message}
)
if response.status_code != 200:
print(f"Error communicating with Rasa: {response.status_code}")
return jsonify({"error": "Error communicating with Rasa"}), 500
bot_response = response.json()
print(f"Received response from Rasa: {bot_response}")
# Save user message and bot response to MySQL
save_chat_to_db(user_id, conversation_id, user_message, bot_response[0]['text'])
return jsonify(bot_response)
To store the conversation history, we’ll create a function that saves each user message and bot response in the MySQL database.
def save_chat_to_db(user_id, conversation_id, user_message, bot_response):
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
cursor.execute("""
INSERT INTO messages (conversation_id, user_message, bot_response, timestamp)
VALUES (%s, %s, %s, %s)
""", (conversation_id, user_message, bot_response, datetime.now(timezone.utc)))
conn.commit()
cursor.close()
conn.close()
print(f"Saved chat to DB: user_id={user_id}, conversation_id={conversation_id}, user_message={user_message}, bot_response={bot_response}")
We’ll also create an endpoint to retrieve the chat history for a user and their conversation ID, so the frontend can display previous messages.
Let’s go over the get_chat_history
function. This helper function retrieves all messages between the user and the chatbot for a specific conversation, sorted by the timestamp.
def get_chat_history(user_id, conversation_id):
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor(dictionary=True)
cursor.execute("""
SELECT user_message, bot_response
FROM messages
WHERE conversation_id = %s
ORDER BY timestamp ASC
""", (conversation_id,))
history = cursor.fetchall()
cursor.close()
conn.close()
print(f"Retrieved chat history for user {user_id} in conversation {conversation_id}: {history}")
return history
Now, let’s add the final backend endpoints for managing chat history and conversations. This will enable our frontend to retrieve past conversations, start new ones, and delete conversations as needed.
The /history
route is a GET
endpoint. It retrieves the chat history based on a user_id
and a specific conversation_id
. If either parameter is missing, it returns an error message.
@app.route('/history', methods=['GET'])
def chat_history():
user_id = request.args.get("user_id")
conversation_id = request.args.get("conversation_id") # Get conversation_id from query params
if not user_id or not conversation_id:
return jsonify({"error": "No user_id or conversation_id provided"}), 400
history = get_chat_history(user_id, conversation_id)
return jsonify(history)
Our next endpoint is /new_conversation
, a POST
endpoint that generates a new conversation ID. We save this ID along with the user ID and a timestamp to the database, allowing us to retrieve it later.
@app.route('/new_conversation', methods=['POST'])
def new_conversation():
data = request.json
user_id = data.get("user_id")
if not user_id:
return jsonify({"error": "No user_id provided"}), 400
# Create a new conversation ID (could be a UUID or a timestamp-based string)
conversation_id = str(datetime.now(timezone.utc).timestamp()) # Use timezone-aware datetime
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
cursor.execute("""
INSERT INTO conversations (user_id, conversation_id, timestamp)
VALUES (%s, %s, %s)
""", (user_id, conversation_id, datetime.now(timezone.utc)))
conn.commit()
cursor.close()
conn.close()
return jsonify({"conversation_id": conversation_id})
Next, we have a GET
endpoint called /conversations
. This retrieves a list of conversations associated with a user ID, helping us populate the sidebar with past conversations.
@app.route('/conversations', methods=['GET'])
def get_conversations():
user_id = request.args.get("user_id")
if not user_id:
return jsonify({"error": "No user_id provided"}), 400
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor(dictionary=True)
cursor.execute("""
SELECT conversation_id
FROM conversations
WHERE user_id = %s
""", (user_id,))
conversations = cursor.fetchall()
cursor.close()
conn.close()
return jsonify(conversations)
Finally, we have the DELETE
endpoint at /conversation
. This takes in the user_id
and conversation_id
and deletes the corresponding records from both the messages
and conversations
tables.
@app.route('/conversation', methods=['DELETE'])
def delete_conversation():
data = request.json
user_id = data.get("user_id")
conversation_id = data.get("conversation_id")
if not user_id or not conversation_id:
return jsonify({"error": "No user_id or conversation_id provided"}), 400
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
cursor.execute("""
DELETE FROM messages WHERE conversation_id = %s
""", (conversation_id,))
cursor.execute("""
DELETE FROM conversations WHERE conversation_id = %s
""", (conversation_id,))
conn.commit()
cursor.close()
conn.close()
return jsonify({"message": "Conversation deleted"})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, debug=True)
Create Database
To install the MySQL connector, simply run the following command in your terminal:"*
pip install mysql-connector-python
Let’s start by opening MySQL Workbench. If you haven’t already, connect to your MySQL server by clicking on the connection.
CREATE DATABASE chattybot;
USE chattybot
This will create DB named chattybot.
Create Tables in DB.
CREATE TABLE `conversations` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) NOT NULL,
`conversation_id` varchar(255) NOT NULL,
`timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `conversation_id` (`conversation_id`)
)
CREATE TABLE `messages` (
`id` int NOT NULL AUTO_INCREMENT,
`conversation_id` varchar(255) NOT NULL,
`user_message` text,
`bot_response` text,
`timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `conversation_id` (`conversation_id`),
CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`conversation_id`) REFERENCES `conversations` (`conversation_id`)
)
Once both tables are created, we’re all set to store and retrieve conversation data from our chatbot!
Creating the Frontend
Create components
folder in src
dir. And Then Create three files in it:
ChatSidebar.jsx
ChatWindow.jsx
ChatApp.jsx
The ChatSidebar
component will serve as the navigation for all conversations in our chatbot. It lets the user toggle between different chat sessions, delete conversations, and start a new chat.
Now that we have the ChatSidebar
component, let’s move on to the main interaction area, which is the ChatWindow
. This is where the user can send and receive messages with the bot.
The ChatWindow
component is where the conversation between the user and the bot happens. It manages the chat messages, displays them in a scrollable window, and provides an input form for the user to send messages.
We’ve built the ChatSidebar
and the ChatWindow
components. Now it’s time to bring everything together in our main component: ChatApp
. This is where all the interaction logic between the user, the bot, and our backend happens.
Now that we’ve completed our main ChatApp
component, let’s wire everything together in our root file—App.js
.
import React from 'react';
import ChatApp from './components/ChatApp';
import './App.css';
const App = () => {
return (
<div className="App">
<ChatApp />
</div>
);
};
export default App;
Here, we simply import the ChatApp
component and return it inside the App
function. This makes ChatApp
the central component of our application."*
So that’s pretty much all for this article. I hope it helped you. So I’ll see you in the next one. Till then bye bye.