Code icon

The App is Under a Quick Maintenance

We apologize for the inconvenience. Please come back later

Menu iconMenu iconOpenAI API Bible Volume 2
OpenAI API Bible Volume 2

Chapter 4: Building a Simple Chatbot with Memory

4.1 Flask and Streamlit Implementations

In this comprehensive chapter, you'll learn to build a sophisticated yet beginner-friendly chatbot that demonstrates advanced memory capabilities. Unlike simple chatbots that only respond to the last message, this implementation will maintain context throughout entire conversations, allowing for more natural and contextually aware interactions. You'll work with two powerful Python frameworks for web applications: Flask, a flexible micro-framework that gives you complete control over your application, and Streamlit, a modern framework designed for rapid application development.

The chapter provides detailed, step-by-step guidance for both frameworks, allowing you to choose the approach that best suits your needs. Whether you select Flask's granular control or Streamlit's streamlined development process, you'll learn how to integrate your chatbot with OpenAI's GPT-4o model, leveraging its advanced natural language processing capabilities for intelligent conversations.

Upon completing this chapter, you'll have created a fully functional chatbot with these essential features:

  • Runs in a local web app - Your chatbot will operate through a web interface that you can access from any browser on your local machine
  • Handles user input in real time - The application will process and respond to user messages immediately, creating a smooth conversation flow
  • Maintains multi-turn conversation memory - Your chatbot will remember previous exchanges and use this context to provide more relevant and coherent responses
  • Uses OpenAI's Chat Completions API to craft responses - You'll learn how to integrate with OpenAI's powerful API to generate human-like responses based on conversation context

We'll begin with 11.1 Flask and Streamlit Implementations, providing an in-depth exploration of both frameworks and guiding you through the setup process for each approach. You'll learn the specific advantages and considerations for both implementations, enabling you to make an informed choice for your project.

Let's dive deep into these powerful web development tools to understand their architectures, capabilities, and use cases. Each framework offers unique advantages for building web applications, and understanding their characteristics will help you make an informed decision for your chatbot implementation:

Flask is a lightweight and versatile micro web framework for Python. It's called "micro" because it provides just the essential components you need to build a web application, giving you complete control over your project's architecture. Key features include:

  • Routing system for handling HTTP requests
  • Template engine for dynamic HTML generation
  • Built-in development server
  • Extensive documentation and large community support
  • Easy integration with various extensions

Streamlit is a modern, Python-based framework designed specifically for creating data applications with minimal effort. It transforms Python scripts into shareable web applications through simple API calls. Notable features include:

  • Built-in widgets for interactive data visualization
  • Automatic hot-reloading during development
  • Native support for machine learning frameworks
  • Simple deployment process
  • Real-time user interaction handling

Both frameworks excel in different scenarios — Flask offers granular control and flexibility for custom web applications, while Streamlit specializes in rapid prototyping and data-focused applications. You're welcome to explore either or both approaches as we proceed with implementation. Let's walk through a detailed implementation of each to help you understand their unique strengths.

Choosing Between Flask and Streamlit

Consider trying both frameworks to gain valuable experience. Flask will give you deeper insights into web development concepts like routing, templates, and HTTP requests. Meanwhile, Streamlit excels at quick prototyping and is perfect when you need to demonstrate functionality rapidly without spending time on frontend development.

4.1.1 Choosing Between Flask and Streamlit: A Detailed Comparison

Let's start with a comprehensive exploration of the key considerations when choosing between Flask and Streamlit for your chatbot project. Both frameworks offer distinct advantages and potential trade-offs that can significantly impact your development experience and final application. Understanding these differences will help you make an informed decision that aligns with your project goals, technical requirements, and development timeline.

Flask and Streamlit represent two different philosophical approaches to web application development. Flask embodies the microframework concept, providing developers with complete control and flexibility, while Streamlit emphasizes rapid development and immediate results through its streamlined, opinionated approach. The choice between them often depends on factors such as project complexity, required customization, deployment requirements, and your team's technical expertise.

When deciding between Flask and Streamlit for your chatbot project, let's explore these key factors in detail:

  • Flask requires more initial setup but offers greater control:
    • You'll need to create and configure routes to handle different URL endpoints
    • Setting up HTML templates requires understanding of frontend development
    • Manual handling of HTTP requests gives you fine-grained control over data flow
    • Session management and user authentication need custom implementation
    • This level of control is powerful but requires deeper web development expertise and more development time
  • Streamlit enables rapid prototyping with minimal configuration:
    • Simple Python functions can create interactive web elements instantly
    • Built-in state management handles data persistence automatically
    • No need to understand HTML, CSS, or JavaScript
    • Interactive widgets are available out-of-the-box
    • Perfect for quick prototypes and data-focused applications where speed of development is crucial
  • Application Requirements
    • Choose Flask for complex, custom web applications:
      • Perfect for projects requiring detailed customization of user interface and backend logic
      • Ideal when you need granular control over authentication systems and user management
      • Excellent for applications with complex routing requirements and multiple endpoints
      • Best choice when your app needs to integrate with various external services and APIs
      • Suitable for projects that may need to scale significantly in the future
      • Recommended when you have experienced developers who understand web development principles
    • Pick Streamlit for data-focused apps needing quick deployment:
      • Excellent for data visualization projects and analytics dashboards
      • Perfect for rapid prototyping of machine learning model interfaces
      • Ideal for projects where data scientists need to quickly demonstrate results
      • Great for applications that primarily focus on data presentation and interaction
      • Suitable for teams that want to minimize frontend development overhead
      • Best when you need to quickly iterate on features based on user feedback
  • Flask provides complete frontend customization freedom:
    • Design fully custom interfaces using HTML, CSS, and JavaScript
    • Create pixel-perfect layouts and brand-specific designs
    • Implement complex interactions and animations
    • Build responsive designs that work across all devices
    • Add custom JavaScript libraries and frameworks as needed
    • Create unique user experiences without limitations
  • Streamlit offers pre-built components ideal for data visualization:
    • Rich set of ready-to-use widgets (charts, graphs, tables)
    • Interactive elements like sliders, buttons, and input fields
    • Real-time data updates and visualizations
    • Built-in support for popular data visualization libraries
    • Simple API for creating complex interfaces without frontend expertise
    • Rapid prototyping capabilities for data-driven applications
  • Long-term Scalability and System Architecture
    • Flask's flexibility enables robust scalability:
      • Modular architecture allows adding new features without disrupting existing code
      • Supports horizontal scaling across multiple servers for increased capacity
      • Easy integration with load balancers and microservices
      • Compatible with various database systems (SQL, NoSQL, distributed databases)
      • Allows implementation of complex caching strategies
      • Supports custom middleware for performance optimization
      • Enables sophisticated authentication and authorization systems
    • Streamlit's architecture has specific limitations:
      • Built primarily for single-page applications and data dashboards
      • May face performance challenges with large concurrent user bases
      • Limited options for complex routing and URL management
      • Restricted ability to implement complex state management
      • Can become harder to maintain as business logic grows
      • Less suitable for microservices architecture
      • May require complete rewrite when scaling beyond certain complexity

4.1.2 Flask

Flask is a powerful and versatile micro web framework for Python that has revolutionized web development with its elegant simplicity and remarkable flexibility. When we call it a "micro" framework, this doesn't mean it's limited in capabilities - rather, it follows a minimalist philosophy where developers start with a lean core and build up exactly what they need. This thoughtful approach eliminates unnecessary complexity while giving developers unprecedented control over their application's architecture. Flask empowers developers to create everything from simple APIs to complex web applications, all while maintaining clean, maintainable code.

Let's dive deep into Flask's key features and understand why they make it such a powerful choice:

  • Routing system for handling HTTP requests - Flask's sophisticated routing mechanism goes beyond simple URL mapping. It provides a decorator-based syntax that makes creating RESTful APIs intuitive, supports dynamic URL patterns with variable rules, and allows for complex URL hierarchies. This flexibility enables developers to structure their applications logically while maintaining clean URL schemes.
  • Template engine for dynamic HTML generation - The integration with Jinja2, one of Python's most powerful templating engines, offers far more than basic HTML generation. It provides template inheritance for DRY (Don't Repeat Yourself) code, custom filters for data manipulation, context processors for global variables, and macro support for reusable HTML components. These features make it possible to create sophisticated, maintainable frontend code without sacrificing flexibility.
  • Built-in development server - Flask's development server is a sophisticated tool that goes beyond basic HTTP serving. It includes features like automatic reloading when code changes, detailed debug pages with interactive stack traces, and the ability to handle multiple concurrent requests. While perfect for development, Flask seamlessly transitions to production-grade servers like Gunicorn or uWSGI when you're ready to deploy.
  • Extensive documentation and large community support - Flask's documentation is not just comprehensive - it's a masterclass in Python web development. Each feature is thoroughly explained with practical examples, best practices, and security considerations. The vibrant community contributes countless extensions, tutorials, and solutions to common problems, making Flask one of the most well-supported frameworks in the Python ecosystem.
  • Easy integration with various extensions - Flask's extension system is a testament to its excellent design. Through a consistent API, extensions can add sophisticated features like database ORM support (SQLAlchemy), form validation (WTForms), user authentication (Flask-Login), API documentation (Swagger/OpenAPI), and much more. These extensions maintain Flask's lightweight nature while providing enterprise-grade functionality when needed.

4.1.3 Flask Chatbot Implementation

This example implements a simple chatbot using Flask and OpenAI's API. The chatbot takes user input via a web form, sends it to OpenAI's GPT-4o model, and displays the model's response in the browser.  It also maintains a basic conversation history.

Step 1: Install Dependencies

pip install flask openai python-dotenv
  • flask: A web framework for building the chatbot application.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Flask App (app.py)

from flask import Flask, request, render_template, session
import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

app = Flask(__name__)
app.secret_key = os.urandom(24)  # Required for session

@app.route("/", methods=["GET", "POST"])
def chat():
    # Use session to store conversation history
    if "conversation_history" not in session:
        session["conversation_history"] = []

    conversation_history = session["conversation_history"]

    if request.method == "POST":
        user_input = request.form["user_input"]
        conversation_history.append({"role": "user", "content": user_input})

        try:
            response = openai.ChatCompletion.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."}
                ] + conversation_history
            )
            assistant_reply = response.choices[0].message.content
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history #save the updated history

        except openai.error.OpenAIError as e:
            assistant_reply = f"Error: {e}"
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history

        return render_template("chat.html", reply=assistant_reply)

    else:
        # Reset the history.  Important for starting a new chat.
        session["conversation_history"] = []
        assistant_reply = "" # Initialize
        return render_template("chat.html", reply=assistant_reply)

if __name__ == "__main__":
    app.run(debug=True)

Code breakdown

  • from flask import ...: Imports necessary modules from Flask.
    • Flask: The Flask class to create the web application.
    • request: To access incoming request data (form data).
    • render_template: To render HTML templates.
    • session: To manage user sessions. Conversation history is now stored in the session.
  • import openai: Imports the OpenAI library.
  • import os: Imports the os module for environment variables.
  • from dotenv import load_dotenv: Imports load_dotenv to load variables from a .env file.
  • load_dotenv(): Loads the environment variables from a .env file.
  • openai.api_key = os.getenv("OPENAI_API_KEY"): Retrieves the OpenAI API key from the environment variable OPENAI_API_KEY and sets it for the OpenAI library.
  • app = Flask(__name__): Creates a Flask application instance.
  • app.secret_key = os.urandom(24)Important: Sets a secret key for the Flask application. This is required to use sessions. os.urandom(24) generates a random, cryptographically secure key.
  • @app.route("/", methods=["GET", "POST"]): This decorator defines a route for the root URL ("/"). The chat() function will handle both GET and POST requests to this URL.
  • def chat():: The function that handles requests to the root URL.
  • if "conversation_history" not in session: session["conversation_history"] = []: This is crucial for persisting the conversation history across requests. The conversation history is stored as a list of dictionaries in the user's session. If conversation_history is not in the session, it initializes it as an empty list.
  • conversation_history = session["conversation_history"]: Gets the conversation history from the session.
  • if request.method == "POST":: Handles POST requests, which occur when the user submits the form.
    • user_input = request.form["user_input"]: Gets the user's input from the form data. The input field in the HTML template is named "user_input".
    • conversation_history.append({"role": "user", "content": user_input}): Appends the user's input to the conversation history.
    • try...except openai.error.OpenAIError as e: Includes error handling for OpenAI API errors.
    • response = openai.ChatCompletion.create(...): Calls the OpenAI API to get a response from the GPT-4o model.
      • model: Specifies the language model to use ("gpt-4o").
      • messages: A list of message dictionaries representing the conversation history. The system message is included at the beginning, followed by the conversation history.
    • assistant_reply = response.choices[0].message.content: Extracts the assistant's reply from the API response.
    • conversation_history.append({"role": "assistant", "content": assistant_reply}): Appends the assistant's reply to the conversation history.
    • session["conversation_history"] = conversation_historyCrucial: Updates the conversation_history in the session, so it's preserved for the next turn of the conversation.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template and passes the assistant's reply to the template.
  • else:: Handles GET requests, which occur when the user initially loads the page.
    • session["conversation_history"] = []: Reset the history when the user loads the page.
    • assistant_reply = "": Initializes assistant_reply to an empty string.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template with an empty reply.
  • if __name__ == "__main__":: This ensures that the Flask development server is started only when the script is executed directly (not when imported as a module).
  • app.run(debug=True): Starts the Flask development server in debug mode. Debug mode provides helpful error messages and automatic reloading when you make changes to the code.

Step 3: Create a Simple HTML Template (templates/chat.html)

<!DOCTYPE html>
<html>
<head><title>Flask Chatbot</title></head>
<body>
  <h2>GPT-4o Chatbot</h2>
  <form method="post">
    <textarea name="user_input" rows="4" cols="50"></textarea><br>
    <input type="submit" value="Send">
  </form>
  <hr>
  <p><strong>Assistant:</strong> {{ reply }}</p>
</body>
</html>

Code breakdown:

  • <!DOCTYPE html>: Declares the document type as HTML5.
  • <html>: The root element of the HTML document.
  • <head>: Contains metadata about the HTML document.
    • <title>: Specifies the title of the HTML page, which is shown in the browser's title bar or tab.
  • <body>: Contains the visible content of the HTML page.
    • <h2>: Defines a level 2 heading.
    • <form method="post">: Defines an HTML form that submits data using the POST method.
      • <textarea>: A multi-line text input field where the user can enter their query. The name attribute is set to "user_input", which is used to access the input in the Flask code.
      • <input type="submit" value="Send">: A submit button that sends the form data to the server.
    • <hr>: A horizontal rule, used to separate content.
    • <p>: A paragraph element to display the assistant's reply.
      • <strong>: Defines strong text (usually displayed in bold).
      • {{ reply }}: A Jinja template variable that will be replaced with the value of the reply variable passed from the Flask code.
  • templates: Flask, by default, looks for HTML templates in a folder named "templates" in the same directory as your app.py file. So, this file should be saved as templates/chat.html.

This example creates a robust, functional chatbot application. To implement it, create a templates folder in the same directory as your app.py file, save the HTML template as chat.html inside that folder, and set the OPENAI_API_KEY environment variable.

4.1.4 Streamlit

Streamlit represents a revolutionary advancement in data application development. This Python-based framework has fundamentally transformed how developers approach web application creation. At its core, Streamlit embodies a philosophy of simplicity and efficiency, enabling developers to convert standard Python scripts into sophisticated web applications with minimal code overhead.

What sets it apart from conventional web frameworks is its innovative approach - while traditional frameworks demand extensive expertise in HTML, CSS, and JavaScript, Streamlit introduces a paradigm shift by providing an abstraction layer through its intuitive API calls. This design choice has made it an invaluable tool, particularly for data scientists and machine learning engineers who can now focus on their core competencies while creating professional-grade interactive demonstrations.

Let's explore its comprehensive feature set in detail:

  • Built-in widgets for interactive data visualization - Streamlit's extensive component library includes sophisticated charts, interactive graphs, customizable sliders, and versatile input fields. These pre-built components eliminate the need for frontend development expertise, allowing creators to focus on data presentation rather than web development intricacies. The framework handles all the complex rendering and state management behind the scenes.
  • Automatic hot-reloading during development - Streamlit's development environment features an intelligent file watching system that monitors your source code for changes. When modifications are detected, it automatically refreshes the web application, creating a seamless development experience. This feature significantly reduces development time by eliminating the traditional save-refresh cycle common in web development.
  • Native support for machine learning frameworks - The framework's deep integration with popular machine learning libraries such as TensorFlow, PyTorch, and scikit-learn goes beyond simple compatibility. It provides specialized components and optimizations for ML workflows, making it an ideal platform for creating interactive model demonstrations, real-time prediction interfaces, and educational tools for machine learning concepts.
  • Simple deployment process - Streamlit's deployment infrastructure has been carefully designed to minimize operational complexity. Whether using Streamlit's own cloud platform or alternative hosting services, the deployment process has been streamlined to require minimal configuration. This approach ensures that developers can quickly share their applications with stakeholders without getting bogged down in deployment technicalities.
  • Real-time user interaction handling - The framework incorporates sophisticated state management and caching mechanisms that operate seamlessly in the background. These systems ensure optimal performance during complex user interactions, managing data flow and component updates efficiently without requiring developers to implement custom backend logic. This results in responsive applications that can handle complex computational tasks while maintaining smooth user experiences.

4.1.5 Streamlit Chatbot Implementation

This example creates a chatbot using Streamlit and OpenAI's API. It's a simple application that allows users to interact with the GPT-4o model through a chat interface. The application maintains chat history using Streamlit's session state. This enhanced version includes error handling, a system prompt, and a sidebar for additional settings.

  • *Step 1: Install Dependencies
pip install streamlit openai python-dotenv

Code breakdown:

  • streamlit: A Python library for creating interactive web applications, in this case, the chatbot interface.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Streamlit App (chatbot.py)

import streamlit as st
import openai
import os
from dotenv import load_dotenv
import time  # For handling potential API errors


load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")


# --- Configuration ---
SYSTEM_PROMPT = "You are a helpful, friendly, and knowledgeable assistant.  Answer all questions clearly and concisely."
DEFAULT_MODEL = "gpt-4o"
MAX_CONTEXT_MESSAGES = 10  # Limit conversation history


# --- Helper Functions ---
def get_openai_response(messages, model=DEFAULT_MODEL):
    """
    Sends a message to OpenAI's Chat API and handles potential errors.
    Args:
        messages: A list of message dictionaries.
        model: The OpenAI model to use.  Defaults to gpt-4o
    Returns:
        The assistant's reply, or an error message.
    """
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            timeout=60,  # Add a timeout to prevent hanging
        )
        return response.choices[0].message.content
    except openai.error.OpenAIError as e:
        st.error(f"OpenAI API Error: {e}")
        return f"Sorry, I encountered an error: {e}"
    except Exception as e:
        st.error(f"An unexpected error occurred: {e}")
        return "Sorry, I encountered an unexpected error."



def initialize_session_state():
    """Initializes session state variables."""
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    if "display_messages" not in st.session_state:
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}] # Separate list for displayed messages
    if "model" not in st.session_state:
        st.session_state.model = DEFAULT_MODEL


# --- Main App ---
def main():
    initialize_session_state()

    # --- Sidebar ---
    st.sidebar.header("Settings")
    model = st.sidebar.selectbox("Model", ["gpt-4o", "gpt-3.5-turbo"], index=0)  # Add model selection
    st.session_state.model = model #update the model
    clear_history = st.sidebar.button("Clear Chat History")
    if clear_history:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # Clear displayed messages too
        st.rerun()  # Force a rerun to update the display

    st.title("🧠 GPT-4o Chatbot with Memory") #moved title

    # Display previous messages
    for message in st.session_state.display_messages:
        if message["role"] != "system":  # Don't display the system prompt
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    # User input
    user_input = st.chat_input("You:", key="user_input")  # Use key for chat_input


    if user_input:
        st.session_state.messages.append({"role": "user", "content": user_input})
        st.session_state.display_messages.append({"role": "user", "content": user_input}) # Also add to display list

        # Get assistant reply
        reply = get_openai_response(st.session_state.messages, model=st.session_state.model)
        st.session_state.messages.append({"role": "assistant", "content": reply})
        st.session_state.display_messages.append({"role": "assistant", "content": reply}) # Also add to display list

        with st.chat_message("assistant"):
            st.markdown(reply)

        # Keep only the last MAX_CONTEXT_MESSAGES messages (including system prompt)
        st.session_state.messages = st.session_state.messages[:1] + st.session_state.messages[-MAX_CONTEXT_MESSAGES + 1:]
        st.rerun() # Force a rerun to update the display



if __name__ == "__main__":
    main()

Let's break down this Streamlit chatbot code:

1. Imports and Setup

  • Imports essential libraries (streamlit, openai, os, dotenv) for chat functionality
  • Loads environment variables and sets up OpenAI API key

2. Configuration

  • Defines system prompt for the chatbot's personality
  • Sets default model (gpt-4o) and maximum context messages (10)

3. Helper Functions

  • get_openai_response(): Handles API communication with OpenAI
    • Includes error handling for API issues
    • Sets 60-second timeout
    • Returns either the response or error message
  • initialize_session_state(): Sets up Streamlit's session state
    • Creates message history for API calls
    • Maintains separate display messages
    • Initializes model selection

4. Main Application

  • Sidebar with settings:
    • Model selection dropdown
    • Clear chat history button
  • Chat interface:
    • Displays message history
    • Handles user input
    • Shows bot responses
    • Maintains conversation context within message limit

5. Key Features

  • Memory management through session state
  • Error handling for API calls
  • Message history limitation for performance
  • Real-time chat updates using Streamlit's chat components

This example provides an user-friendly chatbot with better error handling, a system prompt, and a limit on the conversation history.  It also allows the user to select the model.

4.1 Flask and Streamlit Implementations

In this comprehensive chapter, you'll learn to build a sophisticated yet beginner-friendly chatbot that demonstrates advanced memory capabilities. Unlike simple chatbots that only respond to the last message, this implementation will maintain context throughout entire conversations, allowing for more natural and contextually aware interactions. You'll work with two powerful Python frameworks for web applications: Flask, a flexible micro-framework that gives you complete control over your application, and Streamlit, a modern framework designed for rapid application development.

The chapter provides detailed, step-by-step guidance for both frameworks, allowing you to choose the approach that best suits your needs. Whether you select Flask's granular control or Streamlit's streamlined development process, you'll learn how to integrate your chatbot with OpenAI's GPT-4o model, leveraging its advanced natural language processing capabilities for intelligent conversations.

Upon completing this chapter, you'll have created a fully functional chatbot with these essential features:

  • Runs in a local web app - Your chatbot will operate through a web interface that you can access from any browser on your local machine
  • Handles user input in real time - The application will process and respond to user messages immediately, creating a smooth conversation flow
  • Maintains multi-turn conversation memory - Your chatbot will remember previous exchanges and use this context to provide more relevant and coherent responses
  • Uses OpenAI's Chat Completions API to craft responses - You'll learn how to integrate with OpenAI's powerful API to generate human-like responses based on conversation context

We'll begin with 11.1 Flask and Streamlit Implementations, providing an in-depth exploration of both frameworks and guiding you through the setup process for each approach. You'll learn the specific advantages and considerations for both implementations, enabling you to make an informed choice for your project.

Let's dive deep into these powerful web development tools to understand their architectures, capabilities, and use cases. Each framework offers unique advantages for building web applications, and understanding their characteristics will help you make an informed decision for your chatbot implementation:

Flask is a lightweight and versatile micro web framework for Python. It's called "micro" because it provides just the essential components you need to build a web application, giving you complete control over your project's architecture. Key features include:

  • Routing system for handling HTTP requests
  • Template engine for dynamic HTML generation
  • Built-in development server
  • Extensive documentation and large community support
  • Easy integration with various extensions

Streamlit is a modern, Python-based framework designed specifically for creating data applications with minimal effort. It transforms Python scripts into shareable web applications through simple API calls. Notable features include:

  • Built-in widgets for interactive data visualization
  • Automatic hot-reloading during development
  • Native support for machine learning frameworks
  • Simple deployment process
  • Real-time user interaction handling

Both frameworks excel in different scenarios — Flask offers granular control and flexibility for custom web applications, while Streamlit specializes in rapid prototyping and data-focused applications. You're welcome to explore either or both approaches as we proceed with implementation. Let's walk through a detailed implementation of each to help you understand their unique strengths.

Choosing Between Flask and Streamlit

Consider trying both frameworks to gain valuable experience. Flask will give you deeper insights into web development concepts like routing, templates, and HTTP requests. Meanwhile, Streamlit excels at quick prototyping and is perfect when you need to demonstrate functionality rapidly without spending time on frontend development.

4.1.1 Choosing Between Flask and Streamlit: A Detailed Comparison

Let's start with a comprehensive exploration of the key considerations when choosing between Flask and Streamlit for your chatbot project. Both frameworks offer distinct advantages and potential trade-offs that can significantly impact your development experience and final application. Understanding these differences will help you make an informed decision that aligns with your project goals, technical requirements, and development timeline.

Flask and Streamlit represent two different philosophical approaches to web application development. Flask embodies the microframework concept, providing developers with complete control and flexibility, while Streamlit emphasizes rapid development and immediate results through its streamlined, opinionated approach. The choice between them often depends on factors such as project complexity, required customization, deployment requirements, and your team's technical expertise.

When deciding between Flask and Streamlit for your chatbot project, let's explore these key factors in detail:

  • Flask requires more initial setup but offers greater control:
    • You'll need to create and configure routes to handle different URL endpoints
    • Setting up HTML templates requires understanding of frontend development
    • Manual handling of HTTP requests gives you fine-grained control over data flow
    • Session management and user authentication need custom implementation
    • This level of control is powerful but requires deeper web development expertise and more development time
  • Streamlit enables rapid prototyping with minimal configuration:
    • Simple Python functions can create interactive web elements instantly
    • Built-in state management handles data persistence automatically
    • No need to understand HTML, CSS, or JavaScript
    • Interactive widgets are available out-of-the-box
    • Perfect for quick prototypes and data-focused applications where speed of development is crucial
  • Application Requirements
    • Choose Flask for complex, custom web applications:
      • Perfect for projects requiring detailed customization of user interface and backend logic
      • Ideal when you need granular control over authentication systems and user management
      • Excellent for applications with complex routing requirements and multiple endpoints
      • Best choice when your app needs to integrate with various external services and APIs
      • Suitable for projects that may need to scale significantly in the future
      • Recommended when you have experienced developers who understand web development principles
    • Pick Streamlit for data-focused apps needing quick deployment:
      • Excellent for data visualization projects and analytics dashboards
      • Perfect for rapid prototyping of machine learning model interfaces
      • Ideal for projects where data scientists need to quickly demonstrate results
      • Great for applications that primarily focus on data presentation and interaction
      • Suitable for teams that want to minimize frontend development overhead
      • Best when you need to quickly iterate on features based on user feedback
  • Flask provides complete frontend customization freedom:
    • Design fully custom interfaces using HTML, CSS, and JavaScript
    • Create pixel-perfect layouts and brand-specific designs
    • Implement complex interactions and animations
    • Build responsive designs that work across all devices
    • Add custom JavaScript libraries and frameworks as needed
    • Create unique user experiences without limitations
  • Streamlit offers pre-built components ideal for data visualization:
    • Rich set of ready-to-use widgets (charts, graphs, tables)
    • Interactive elements like sliders, buttons, and input fields
    • Real-time data updates and visualizations
    • Built-in support for popular data visualization libraries
    • Simple API for creating complex interfaces without frontend expertise
    • Rapid prototyping capabilities for data-driven applications
  • Long-term Scalability and System Architecture
    • Flask's flexibility enables robust scalability:
      • Modular architecture allows adding new features without disrupting existing code
      • Supports horizontal scaling across multiple servers for increased capacity
      • Easy integration with load balancers and microservices
      • Compatible with various database systems (SQL, NoSQL, distributed databases)
      • Allows implementation of complex caching strategies
      • Supports custom middleware for performance optimization
      • Enables sophisticated authentication and authorization systems
    • Streamlit's architecture has specific limitations:
      • Built primarily for single-page applications and data dashboards
      • May face performance challenges with large concurrent user bases
      • Limited options for complex routing and URL management
      • Restricted ability to implement complex state management
      • Can become harder to maintain as business logic grows
      • Less suitable for microservices architecture
      • May require complete rewrite when scaling beyond certain complexity

4.1.2 Flask

Flask is a powerful and versatile micro web framework for Python that has revolutionized web development with its elegant simplicity and remarkable flexibility. When we call it a "micro" framework, this doesn't mean it's limited in capabilities - rather, it follows a minimalist philosophy where developers start with a lean core and build up exactly what they need. This thoughtful approach eliminates unnecessary complexity while giving developers unprecedented control over their application's architecture. Flask empowers developers to create everything from simple APIs to complex web applications, all while maintaining clean, maintainable code.

Let's dive deep into Flask's key features and understand why they make it such a powerful choice:

  • Routing system for handling HTTP requests - Flask's sophisticated routing mechanism goes beyond simple URL mapping. It provides a decorator-based syntax that makes creating RESTful APIs intuitive, supports dynamic URL patterns with variable rules, and allows for complex URL hierarchies. This flexibility enables developers to structure their applications logically while maintaining clean URL schemes.
  • Template engine for dynamic HTML generation - The integration with Jinja2, one of Python's most powerful templating engines, offers far more than basic HTML generation. It provides template inheritance for DRY (Don't Repeat Yourself) code, custom filters for data manipulation, context processors for global variables, and macro support for reusable HTML components. These features make it possible to create sophisticated, maintainable frontend code without sacrificing flexibility.
  • Built-in development server - Flask's development server is a sophisticated tool that goes beyond basic HTTP serving. It includes features like automatic reloading when code changes, detailed debug pages with interactive stack traces, and the ability to handle multiple concurrent requests. While perfect for development, Flask seamlessly transitions to production-grade servers like Gunicorn or uWSGI when you're ready to deploy.
  • Extensive documentation and large community support - Flask's documentation is not just comprehensive - it's a masterclass in Python web development. Each feature is thoroughly explained with practical examples, best practices, and security considerations. The vibrant community contributes countless extensions, tutorials, and solutions to common problems, making Flask one of the most well-supported frameworks in the Python ecosystem.
  • Easy integration with various extensions - Flask's extension system is a testament to its excellent design. Through a consistent API, extensions can add sophisticated features like database ORM support (SQLAlchemy), form validation (WTForms), user authentication (Flask-Login), API documentation (Swagger/OpenAPI), and much more. These extensions maintain Flask's lightweight nature while providing enterprise-grade functionality when needed.

4.1.3 Flask Chatbot Implementation

This example implements a simple chatbot using Flask and OpenAI's API. The chatbot takes user input via a web form, sends it to OpenAI's GPT-4o model, and displays the model's response in the browser.  It also maintains a basic conversation history.

Step 1: Install Dependencies

pip install flask openai python-dotenv
  • flask: A web framework for building the chatbot application.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Flask App (app.py)

from flask import Flask, request, render_template, session
import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

app = Flask(__name__)
app.secret_key = os.urandom(24)  # Required for session

@app.route("/", methods=["GET", "POST"])
def chat():
    # Use session to store conversation history
    if "conversation_history" not in session:
        session["conversation_history"] = []

    conversation_history = session["conversation_history"]

    if request.method == "POST":
        user_input = request.form["user_input"]
        conversation_history.append({"role": "user", "content": user_input})

        try:
            response = openai.ChatCompletion.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."}
                ] + conversation_history
            )
            assistant_reply = response.choices[0].message.content
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history #save the updated history

        except openai.error.OpenAIError as e:
            assistant_reply = f"Error: {e}"
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history

        return render_template("chat.html", reply=assistant_reply)

    else:
        # Reset the history.  Important for starting a new chat.
        session["conversation_history"] = []
        assistant_reply = "" # Initialize
        return render_template("chat.html", reply=assistant_reply)

if __name__ == "__main__":
    app.run(debug=True)

Code breakdown

  • from flask import ...: Imports necessary modules from Flask.
    • Flask: The Flask class to create the web application.
    • request: To access incoming request data (form data).
    • render_template: To render HTML templates.
    • session: To manage user sessions. Conversation history is now stored in the session.
  • import openai: Imports the OpenAI library.
  • import os: Imports the os module for environment variables.
  • from dotenv import load_dotenv: Imports load_dotenv to load variables from a .env file.
  • load_dotenv(): Loads the environment variables from a .env file.
  • openai.api_key = os.getenv("OPENAI_API_KEY"): Retrieves the OpenAI API key from the environment variable OPENAI_API_KEY and sets it for the OpenAI library.
  • app = Flask(__name__): Creates a Flask application instance.
  • app.secret_key = os.urandom(24)Important: Sets a secret key for the Flask application. This is required to use sessions. os.urandom(24) generates a random, cryptographically secure key.
  • @app.route("/", methods=["GET", "POST"]): This decorator defines a route for the root URL ("/"). The chat() function will handle both GET and POST requests to this URL.
  • def chat():: The function that handles requests to the root URL.
  • if "conversation_history" not in session: session["conversation_history"] = []: This is crucial for persisting the conversation history across requests. The conversation history is stored as a list of dictionaries in the user's session. If conversation_history is not in the session, it initializes it as an empty list.
  • conversation_history = session["conversation_history"]: Gets the conversation history from the session.
  • if request.method == "POST":: Handles POST requests, which occur when the user submits the form.
    • user_input = request.form["user_input"]: Gets the user's input from the form data. The input field in the HTML template is named "user_input".
    • conversation_history.append({"role": "user", "content": user_input}): Appends the user's input to the conversation history.
    • try...except openai.error.OpenAIError as e: Includes error handling for OpenAI API errors.
    • response = openai.ChatCompletion.create(...): Calls the OpenAI API to get a response from the GPT-4o model.
      • model: Specifies the language model to use ("gpt-4o").
      • messages: A list of message dictionaries representing the conversation history. The system message is included at the beginning, followed by the conversation history.
    • assistant_reply = response.choices[0].message.content: Extracts the assistant's reply from the API response.
    • conversation_history.append({"role": "assistant", "content": assistant_reply}): Appends the assistant's reply to the conversation history.
    • session["conversation_history"] = conversation_historyCrucial: Updates the conversation_history in the session, so it's preserved for the next turn of the conversation.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template and passes the assistant's reply to the template.
  • else:: Handles GET requests, which occur when the user initially loads the page.
    • session["conversation_history"] = []: Reset the history when the user loads the page.
    • assistant_reply = "": Initializes assistant_reply to an empty string.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template with an empty reply.
  • if __name__ == "__main__":: This ensures that the Flask development server is started only when the script is executed directly (not when imported as a module).
  • app.run(debug=True): Starts the Flask development server in debug mode. Debug mode provides helpful error messages and automatic reloading when you make changes to the code.

Step 3: Create a Simple HTML Template (templates/chat.html)

<!DOCTYPE html>
<html>
<head><title>Flask Chatbot</title></head>
<body>
  <h2>GPT-4o Chatbot</h2>
  <form method="post">
    <textarea name="user_input" rows="4" cols="50"></textarea><br>
    <input type="submit" value="Send">
  </form>
  <hr>
  <p><strong>Assistant:</strong> {{ reply }}</p>
</body>
</html>

Code breakdown:

  • <!DOCTYPE html>: Declares the document type as HTML5.
  • <html>: The root element of the HTML document.
  • <head>: Contains metadata about the HTML document.
    • <title>: Specifies the title of the HTML page, which is shown in the browser's title bar or tab.
  • <body>: Contains the visible content of the HTML page.
    • <h2>: Defines a level 2 heading.
    • <form method="post">: Defines an HTML form that submits data using the POST method.
      • <textarea>: A multi-line text input field where the user can enter their query. The name attribute is set to "user_input", which is used to access the input in the Flask code.
      • <input type="submit" value="Send">: A submit button that sends the form data to the server.
    • <hr>: A horizontal rule, used to separate content.
    • <p>: A paragraph element to display the assistant's reply.
      • <strong>: Defines strong text (usually displayed in bold).
      • {{ reply }}: A Jinja template variable that will be replaced with the value of the reply variable passed from the Flask code.
  • templates: Flask, by default, looks for HTML templates in a folder named "templates" in the same directory as your app.py file. So, this file should be saved as templates/chat.html.

This example creates a robust, functional chatbot application. To implement it, create a templates folder in the same directory as your app.py file, save the HTML template as chat.html inside that folder, and set the OPENAI_API_KEY environment variable.

4.1.4 Streamlit

Streamlit represents a revolutionary advancement in data application development. This Python-based framework has fundamentally transformed how developers approach web application creation. At its core, Streamlit embodies a philosophy of simplicity and efficiency, enabling developers to convert standard Python scripts into sophisticated web applications with minimal code overhead.

What sets it apart from conventional web frameworks is its innovative approach - while traditional frameworks demand extensive expertise in HTML, CSS, and JavaScript, Streamlit introduces a paradigm shift by providing an abstraction layer through its intuitive API calls. This design choice has made it an invaluable tool, particularly for data scientists and machine learning engineers who can now focus on their core competencies while creating professional-grade interactive demonstrations.

Let's explore its comprehensive feature set in detail:

  • Built-in widgets for interactive data visualization - Streamlit's extensive component library includes sophisticated charts, interactive graphs, customizable sliders, and versatile input fields. These pre-built components eliminate the need for frontend development expertise, allowing creators to focus on data presentation rather than web development intricacies. The framework handles all the complex rendering and state management behind the scenes.
  • Automatic hot-reloading during development - Streamlit's development environment features an intelligent file watching system that monitors your source code for changes. When modifications are detected, it automatically refreshes the web application, creating a seamless development experience. This feature significantly reduces development time by eliminating the traditional save-refresh cycle common in web development.
  • Native support for machine learning frameworks - The framework's deep integration with popular machine learning libraries such as TensorFlow, PyTorch, and scikit-learn goes beyond simple compatibility. It provides specialized components and optimizations for ML workflows, making it an ideal platform for creating interactive model demonstrations, real-time prediction interfaces, and educational tools for machine learning concepts.
  • Simple deployment process - Streamlit's deployment infrastructure has been carefully designed to minimize operational complexity. Whether using Streamlit's own cloud platform or alternative hosting services, the deployment process has been streamlined to require minimal configuration. This approach ensures that developers can quickly share their applications with stakeholders without getting bogged down in deployment technicalities.
  • Real-time user interaction handling - The framework incorporates sophisticated state management and caching mechanisms that operate seamlessly in the background. These systems ensure optimal performance during complex user interactions, managing data flow and component updates efficiently without requiring developers to implement custom backend logic. This results in responsive applications that can handle complex computational tasks while maintaining smooth user experiences.

4.1.5 Streamlit Chatbot Implementation

This example creates a chatbot using Streamlit and OpenAI's API. It's a simple application that allows users to interact with the GPT-4o model through a chat interface. The application maintains chat history using Streamlit's session state. This enhanced version includes error handling, a system prompt, and a sidebar for additional settings.

  • *Step 1: Install Dependencies
pip install streamlit openai python-dotenv

Code breakdown:

  • streamlit: A Python library for creating interactive web applications, in this case, the chatbot interface.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Streamlit App (chatbot.py)

import streamlit as st
import openai
import os
from dotenv import load_dotenv
import time  # For handling potential API errors


load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")


# --- Configuration ---
SYSTEM_PROMPT = "You are a helpful, friendly, and knowledgeable assistant.  Answer all questions clearly and concisely."
DEFAULT_MODEL = "gpt-4o"
MAX_CONTEXT_MESSAGES = 10  # Limit conversation history


# --- Helper Functions ---
def get_openai_response(messages, model=DEFAULT_MODEL):
    """
    Sends a message to OpenAI's Chat API and handles potential errors.
    Args:
        messages: A list of message dictionaries.
        model: The OpenAI model to use.  Defaults to gpt-4o
    Returns:
        The assistant's reply, or an error message.
    """
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            timeout=60,  # Add a timeout to prevent hanging
        )
        return response.choices[0].message.content
    except openai.error.OpenAIError as e:
        st.error(f"OpenAI API Error: {e}")
        return f"Sorry, I encountered an error: {e}"
    except Exception as e:
        st.error(f"An unexpected error occurred: {e}")
        return "Sorry, I encountered an unexpected error."



def initialize_session_state():
    """Initializes session state variables."""
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    if "display_messages" not in st.session_state:
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}] # Separate list for displayed messages
    if "model" not in st.session_state:
        st.session_state.model = DEFAULT_MODEL


# --- Main App ---
def main():
    initialize_session_state()

    # --- Sidebar ---
    st.sidebar.header("Settings")
    model = st.sidebar.selectbox("Model", ["gpt-4o", "gpt-3.5-turbo"], index=0)  # Add model selection
    st.session_state.model = model #update the model
    clear_history = st.sidebar.button("Clear Chat History")
    if clear_history:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # Clear displayed messages too
        st.rerun()  # Force a rerun to update the display

    st.title("🧠 GPT-4o Chatbot with Memory") #moved title

    # Display previous messages
    for message in st.session_state.display_messages:
        if message["role"] != "system":  # Don't display the system prompt
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    # User input
    user_input = st.chat_input("You:", key="user_input")  # Use key for chat_input


    if user_input:
        st.session_state.messages.append({"role": "user", "content": user_input})
        st.session_state.display_messages.append({"role": "user", "content": user_input}) # Also add to display list

        # Get assistant reply
        reply = get_openai_response(st.session_state.messages, model=st.session_state.model)
        st.session_state.messages.append({"role": "assistant", "content": reply})
        st.session_state.display_messages.append({"role": "assistant", "content": reply}) # Also add to display list

        with st.chat_message("assistant"):
            st.markdown(reply)

        # Keep only the last MAX_CONTEXT_MESSAGES messages (including system prompt)
        st.session_state.messages = st.session_state.messages[:1] + st.session_state.messages[-MAX_CONTEXT_MESSAGES + 1:]
        st.rerun() # Force a rerun to update the display



if __name__ == "__main__":
    main()

Let's break down this Streamlit chatbot code:

1. Imports and Setup

  • Imports essential libraries (streamlit, openai, os, dotenv) for chat functionality
  • Loads environment variables and sets up OpenAI API key

2. Configuration

  • Defines system prompt for the chatbot's personality
  • Sets default model (gpt-4o) and maximum context messages (10)

3. Helper Functions

  • get_openai_response(): Handles API communication with OpenAI
    • Includes error handling for API issues
    • Sets 60-second timeout
    • Returns either the response or error message
  • initialize_session_state(): Sets up Streamlit's session state
    • Creates message history for API calls
    • Maintains separate display messages
    • Initializes model selection

4. Main Application

  • Sidebar with settings:
    • Model selection dropdown
    • Clear chat history button
  • Chat interface:
    • Displays message history
    • Handles user input
    • Shows bot responses
    • Maintains conversation context within message limit

5. Key Features

  • Memory management through session state
  • Error handling for API calls
  • Message history limitation for performance
  • Real-time chat updates using Streamlit's chat components

This example provides an user-friendly chatbot with better error handling, a system prompt, and a limit on the conversation history.  It also allows the user to select the model.

4.1 Flask and Streamlit Implementations

In this comprehensive chapter, you'll learn to build a sophisticated yet beginner-friendly chatbot that demonstrates advanced memory capabilities. Unlike simple chatbots that only respond to the last message, this implementation will maintain context throughout entire conversations, allowing for more natural and contextually aware interactions. You'll work with two powerful Python frameworks for web applications: Flask, a flexible micro-framework that gives you complete control over your application, and Streamlit, a modern framework designed for rapid application development.

The chapter provides detailed, step-by-step guidance for both frameworks, allowing you to choose the approach that best suits your needs. Whether you select Flask's granular control or Streamlit's streamlined development process, you'll learn how to integrate your chatbot with OpenAI's GPT-4o model, leveraging its advanced natural language processing capabilities for intelligent conversations.

Upon completing this chapter, you'll have created a fully functional chatbot with these essential features:

  • Runs in a local web app - Your chatbot will operate through a web interface that you can access from any browser on your local machine
  • Handles user input in real time - The application will process and respond to user messages immediately, creating a smooth conversation flow
  • Maintains multi-turn conversation memory - Your chatbot will remember previous exchanges and use this context to provide more relevant and coherent responses
  • Uses OpenAI's Chat Completions API to craft responses - You'll learn how to integrate with OpenAI's powerful API to generate human-like responses based on conversation context

We'll begin with 11.1 Flask and Streamlit Implementations, providing an in-depth exploration of both frameworks and guiding you through the setup process for each approach. You'll learn the specific advantages and considerations for both implementations, enabling you to make an informed choice for your project.

Let's dive deep into these powerful web development tools to understand their architectures, capabilities, and use cases. Each framework offers unique advantages for building web applications, and understanding their characteristics will help you make an informed decision for your chatbot implementation:

Flask is a lightweight and versatile micro web framework for Python. It's called "micro" because it provides just the essential components you need to build a web application, giving you complete control over your project's architecture. Key features include:

  • Routing system for handling HTTP requests
  • Template engine for dynamic HTML generation
  • Built-in development server
  • Extensive documentation and large community support
  • Easy integration with various extensions

Streamlit is a modern, Python-based framework designed specifically for creating data applications with minimal effort. It transforms Python scripts into shareable web applications through simple API calls. Notable features include:

  • Built-in widgets for interactive data visualization
  • Automatic hot-reloading during development
  • Native support for machine learning frameworks
  • Simple deployment process
  • Real-time user interaction handling

Both frameworks excel in different scenarios — Flask offers granular control and flexibility for custom web applications, while Streamlit specializes in rapid prototyping and data-focused applications. You're welcome to explore either or both approaches as we proceed with implementation. Let's walk through a detailed implementation of each to help you understand their unique strengths.

Choosing Between Flask and Streamlit

Consider trying both frameworks to gain valuable experience. Flask will give you deeper insights into web development concepts like routing, templates, and HTTP requests. Meanwhile, Streamlit excels at quick prototyping and is perfect when you need to demonstrate functionality rapidly without spending time on frontend development.

4.1.1 Choosing Between Flask and Streamlit: A Detailed Comparison

Let's start with a comprehensive exploration of the key considerations when choosing between Flask and Streamlit for your chatbot project. Both frameworks offer distinct advantages and potential trade-offs that can significantly impact your development experience and final application. Understanding these differences will help you make an informed decision that aligns with your project goals, technical requirements, and development timeline.

Flask and Streamlit represent two different philosophical approaches to web application development. Flask embodies the microframework concept, providing developers with complete control and flexibility, while Streamlit emphasizes rapid development and immediate results through its streamlined, opinionated approach. The choice between them often depends on factors such as project complexity, required customization, deployment requirements, and your team's technical expertise.

When deciding between Flask and Streamlit for your chatbot project, let's explore these key factors in detail:

  • Flask requires more initial setup but offers greater control:
    • You'll need to create and configure routes to handle different URL endpoints
    • Setting up HTML templates requires understanding of frontend development
    • Manual handling of HTTP requests gives you fine-grained control over data flow
    • Session management and user authentication need custom implementation
    • This level of control is powerful but requires deeper web development expertise and more development time
  • Streamlit enables rapid prototyping with minimal configuration:
    • Simple Python functions can create interactive web elements instantly
    • Built-in state management handles data persistence automatically
    • No need to understand HTML, CSS, or JavaScript
    • Interactive widgets are available out-of-the-box
    • Perfect for quick prototypes and data-focused applications where speed of development is crucial
  • Application Requirements
    • Choose Flask for complex, custom web applications:
      • Perfect for projects requiring detailed customization of user interface and backend logic
      • Ideal when you need granular control over authentication systems and user management
      • Excellent for applications with complex routing requirements and multiple endpoints
      • Best choice when your app needs to integrate with various external services and APIs
      • Suitable for projects that may need to scale significantly in the future
      • Recommended when you have experienced developers who understand web development principles
    • Pick Streamlit for data-focused apps needing quick deployment:
      • Excellent for data visualization projects and analytics dashboards
      • Perfect for rapid prototyping of machine learning model interfaces
      • Ideal for projects where data scientists need to quickly demonstrate results
      • Great for applications that primarily focus on data presentation and interaction
      • Suitable for teams that want to minimize frontend development overhead
      • Best when you need to quickly iterate on features based on user feedback
  • Flask provides complete frontend customization freedom:
    • Design fully custom interfaces using HTML, CSS, and JavaScript
    • Create pixel-perfect layouts and brand-specific designs
    • Implement complex interactions and animations
    • Build responsive designs that work across all devices
    • Add custom JavaScript libraries and frameworks as needed
    • Create unique user experiences without limitations
  • Streamlit offers pre-built components ideal for data visualization:
    • Rich set of ready-to-use widgets (charts, graphs, tables)
    • Interactive elements like sliders, buttons, and input fields
    • Real-time data updates and visualizations
    • Built-in support for popular data visualization libraries
    • Simple API for creating complex interfaces without frontend expertise
    • Rapid prototyping capabilities for data-driven applications
  • Long-term Scalability and System Architecture
    • Flask's flexibility enables robust scalability:
      • Modular architecture allows adding new features without disrupting existing code
      • Supports horizontal scaling across multiple servers for increased capacity
      • Easy integration with load balancers and microservices
      • Compatible with various database systems (SQL, NoSQL, distributed databases)
      • Allows implementation of complex caching strategies
      • Supports custom middleware for performance optimization
      • Enables sophisticated authentication and authorization systems
    • Streamlit's architecture has specific limitations:
      • Built primarily for single-page applications and data dashboards
      • May face performance challenges with large concurrent user bases
      • Limited options for complex routing and URL management
      • Restricted ability to implement complex state management
      • Can become harder to maintain as business logic grows
      • Less suitable for microservices architecture
      • May require complete rewrite when scaling beyond certain complexity

4.1.2 Flask

Flask is a powerful and versatile micro web framework for Python that has revolutionized web development with its elegant simplicity and remarkable flexibility. When we call it a "micro" framework, this doesn't mean it's limited in capabilities - rather, it follows a minimalist philosophy where developers start with a lean core and build up exactly what they need. This thoughtful approach eliminates unnecessary complexity while giving developers unprecedented control over their application's architecture. Flask empowers developers to create everything from simple APIs to complex web applications, all while maintaining clean, maintainable code.

Let's dive deep into Flask's key features and understand why they make it such a powerful choice:

  • Routing system for handling HTTP requests - Flask's sophisticated routing mechanism goes beyond simple URL mapping. It provides a decorator-based syntax that makes creating RESTful APIs intuitive, supports dynamic URL patterns with variable rules, and allows for complex URL hierarchies. This flexibility enables developers to structure their applications logically while maintaining clean URL schemes.
  • Template engine for dynamic HTML generation - The integration with Jinja2, one of Python's most powerful templating engines, offers far more than basic HTML generation. It provides template inheritance for DRY (Don't Repeat Yourself) code, custom filters for data manipulation, context processors for global variables, and macro support for reusable HTML components. These features make it possible to create sophisticated, maintainable frontend code without sacrificing flexibility.
  • Built-in development server - Flask's development server is a sophisticated tool that goes beyond basic HTTP serving. It includes features like automatic reloading when code changes, detailed debug pages with interactive stack traces, and the ability to handle multiple concurrent requests. While perfect for development, Flask seamlessly transitions to production-grade servers like Gunicorn or uWSGI when you're ready to deploy.
  • Extensive documentation and large community support - Flask's documentation is not just comprehensive - it's a masterclass in Python web development. Each feature is thoroughly explained with practical examples, best practices, and security considerations. The vibrant community contributes countless extensions, tutorials, and solutions to common problems, making Flask one of the most well-supported frameworks in the Python ecosystem.
  • Easy integration with various extensions - Flask's extension system is a testament to its excellent design. Through a consistent API, extensions can add sophisticated features like database ORM support (SQLAlchemy), form validation (WTForms), user authentication (Flask-Login), API documentation (Swagger/OpenAPI), and much more. These extensions maintain Flask's lightweight nature while providing enterprise-grade functionality when needed.

4.1.3 Flask Chatbot Implementation

This example implements a simple chatbot using Flask and OpenAI's API. The chatbot takes user input via a web form, sends it to OpenAI's GPT-4o model, and displays the model's response in the browser.  It also maintains a basic conversation history.

Step 1: Install Dependencies

pip install flask openai python-dotenv
  • flask: A web framework for building the chatbot application.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Flask App (app.py)

from flask import Flask, request, render_template, session
import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

app = Flask(__name__)
app.secret_key = os.urandom(24)  # Required for session

@app.route("/", methods=["GET", "POST"])
def chat():
    # Use session to store conversation history
    if "conversation_history" not in session:
        session["conversation_history"] = []

    conversation_history = session["conversation_history"]

    if request.method == "POST":
        user_input = request.form["user_input"]
        conversation_history.append({"role": "user", "content": user_input})

        try:
            response = openai.ChatCompletion.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."}
                ] + conversation_history
            )
            assistant_reply = response.choices[0].message.content
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history #save the updated history

        except openai.error.OpenAIError as e:
            assistant_reply = f"Error: {e}"
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history

        return render_template("chat.html", reply=assistant_reply)

    else:
        # Reset the history.  Important for starting a new chat.
        session["conversation_history"] = []
        assistant_reply = "" # Initialize
        return render_template("chat.html", reply=assistant_reply)

if __name__ == "__main__":
    app.run(debug=True)

Code breakdown

  • from flask import ...: Imports necessary modules from Flask.
    • Flask: The Flask class to create the web application.
    • request: To access incoming request data (form data).
    • render_template: To render HTML templates.
    • session: To manage user sessions. Conversation history is now stored in the session.
  • import openai: Imports the OpenAI library.
  • import os: Imports the os module for environment variables.
  • from dotenv import load_dotenv: Imports load_dotenv to load variables from a .env file.
  • load_dotenv(): Loads the environment variables from a .env file.
  • openai.api_key = os.getenv("OPENAI_API_KEY"): Retrieves the OpenAI API key from the environment variable OPENAI_API_KEY and sets it for the OpenAI library.
  • app = Flask(__name__): Creates a Flask application instance.
  • app.secret_key = os.urandom(24)Important: Sets a secret key for the Flask application. This is required to use sessions. os.urandom(24) generates a random, cryptographically secure key.
  • @app.route("/", methods=["GET", "POST"]): This decorator defines a route for the root URL ("/"). The chat() function will handle both GET and POST requests to this URL.
  • def chat():: The function that handles requests to the root URL.
  • if "conversation_history" not in session: session["conversation_history"] = []: This is crucial for persisting the conversation history across requests. The conversation history is stored as a list of dictionaries in the user's session. If conversation_history is not in the session, it initializes it as an empty list.
  • conversation_history = session["conversation_history"]: Gets the conversation history from the session.
  • if request.method == "POST":: Handles POST requests, which occur when the user submits the form.
    • user_input = request.form["user_input"]: Gets the user's input from the form data. The input field in the HTML template is named "user_input".
    • conversation_history.append({"role": "user", "content": user_input}): Appends the user's input to the conversation history.
    • try...except openai.error.OpenAIError as e: Includes error handling for OpenAI API errors.
    • response = openai.ChatCompletion.create(...): Calls the OpenAI API to get a response from the GPT-4o model.
      • model: Specifies the language model to use ("gpt-4o").
      • messages: A list of message dictionaries representing the conversation history. The system message is included at the beginning, followed by the conversation history.
    • assistant_reply = response.choices[0].message.content: Extracts the assistant's reply from the API response.
    • conversation_history.append({"role": "assistant", "content": assistant_reply}): Appends the assistant's reply to the conversation history.
    • session["conversation_history"] = conversation_historyCrucial: Updates the conversation_history in the session, so it's preserved for the next turn of the conversation.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template and passes the assistant's reply to the template.
  • else:: Handles GET requests, which occur when the user initially loads the page.
    • session["conversation_history"] = []: Reset the history when the user loads the page.
    • assistant_reply = "": Initializes assistant_reply to an empty string.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template with an empty reply.
  • if __name__ == "__main__":: This ensures that the Flask development server is started only when the script is executed directly (not when imported as a module).
  • app.run(debug=True): Starts the Flask development server in debug mode. Debug mode provides helpful error messages and automatic reloading when you make changes to the code.

Step 3: Create a Simple HTML Template (templates/chat.html)

<!DOCTYPE html>
<html>
<head><title>Flask Chatbot</title></head>
<body>
  <h2>GPT-4o Chatbot</h2>
  <form method="post">
    <textarea name="user_input" rows="4" cols="50"></textarea><br>
    <input type="submit" value="Send">
  </form>
  <hr>
  <p><strong>Assistant:</strong> {{ reply }}</p>
</body>
</html>

Code breakdown:

  • <!DOCTYPE html>: Declares the document type as HTML5.
  • <html>: The root element of the HTML document.
  • <head>: Contains metadata about the HTML document.
    • <title>: Specifies the title of the HTML page, which is shown in the browser's title bar or tab.
  • <body>: Contains the visible content of the HTML page.
    • <h2>: Defines a level 2 heading.
    • <form method="post">: Defines an HTML form that submits data using the POST method.
      • <textarea>: A multi-line text input field where the user can enter their query. The name attribute is set to "user_input", which is used to access the input in the Flask code.
      • <input type="submit" value="Send">: A submit button that sends the form data to the server.
    • <hr>: A horizontal rule, used to separate content.
    • <p>: A paragraph element to display the assistant's reply.
      • <strong>: Defines strong text (usually displayed in bold).
      • {{ reply }}: A Jinja template variable that will be replaced with the value of the reply variable passed from the Flask code.
  • templates: Flask, by default, looks for HTML templates in a folder named "templates" in the same directory as your app.py file. So, this file should be saved as templates/chat.html.

This example creates a robust, functional chatbot application. To implement it, create a templates folder in the same directory as your app.py file, save the HTML template as chat.html inside that folder, and set the OPENAI_API_KEY environment variable.

4.1.4 Streamlit

Streamlit represents a revolutionary advancement in data application development. This Python-based framework has fundamentally transformed how developers approach web application creation. At its core, Streamlit embodies a philosophy of simplicity and efficiency, enabling developers to convert standard Python scripts into sophisticated web applications with minimal code overhead.

What sets it apart from conventional web frameworks is its innovative approach - while traditional frameworks demand extensive expertise in HTML, CSS, and JavaScript, Streamlit introduces a paradigm shift by providing an abstraction layer through its intuitive API calls. This design choice has made it an invaluable tool, particularly for data scientists and machine learning engineers who can now focus on their core competencies while creating professional-grade interactive demonstrations.

Let's explore its comprehensive feature set in detail:

  • Built-in widgets for interactive data visualization - Streamlit's extensive component library includes sophisticated charts, interactive graphs, customizable sliders, and versatile input fields. These pre-built components eliminate the need for frontend development expertise, allowing creators to focus on data presentation rather than web development intricacies. The framework handles all the complex rendering and state management behind the scenes.
  • Automatic hot-reloading during development - Streamlit's development environment features an intelligent file watching system that monitors your source code for changes. When modifications are detected, it automatically refreshes the web application, creating a seamless development experience. This feature significantly reduces development time by eliminating the traditional save-refresh cycle common in web development.
  • Native support for machine learning frameworks - The framework's deep integration with popular machine learning libraries such as TensorFlow, PyTorch, and scikit-learn goes beyond simple compatibility. It provides specialized components and optimizations for ML workflows, making it an ideal platform for creating interactive model demonstrations, real-time prediction interfaces, and educational tools for machine learning concepts.
  • Simple deployment process - Streamlit's deployment infrastructure has been carefully designed to minimize operational complexity. Whether using Streamlit's own cloud platform or alternative hosting services, the deployment process has been streamlined to require minimal configuration. This approach ensures that developers can quickly share their applications with stakeholders without getting bogged down in deployment technicalities.
  • Real-time user interaction handling - The framework incorporates sophisticated state management and caching mechanisms that operate seamlessly in the background. These systems ensure optimal performance during complex user interactions, managing data flow and component updates efficiently without requiring developers to implement custom backend logic. This results in responsive applications that can handle complex computational tasks while maintaining smooth user experiences.

4.1.5 Streamlit Chatbot Implementation

This example creates a chatbot using Streamlit and OpenAI's API. It's a simple application that allows users to interact with the GPT-4o model through a chat interface. The application maintains chat history using Streamlit's session state. This enhanced version includes error handling, a system prompt, and a sidebar for additional settings.

  • *Step 1: Install Dependencies
pip install streamlit openai python-dotenv

Code breakdown:

  • streamlit: A Python library for creating interactive web applications, in this case, the chatbot interface.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Streamlit App (chatbot.py)

import streamlit as st
import openai
import os
from dotenv import load_dotenv
import time  # For handling potential API errors


load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")


# --- Configuration ---
SYSTEM_PROMPT = "You are a helpful, friendly, and knowledgeable assistant.  Answer all questions clearly and concisely."
DEFAULT_MODEL = "gpt-4o"
MAX_CONTEXT_MESSAGES = 10  # Limit conversation history


# --- Helper Functions ---
def get_openai_response(messages, model=DEFAULT_MODEL):
    """
    Sends a message to OpenAI's Chat API and handles potential errors.
    Args:
        messages: A list of message dictionaries.
        model: The OpenAI model to use.  Defaults to gpt-4o
    Returns:
        The assistant's reply, or an error message.
    """
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            timeout=60,  # Add a timeout to prevent hanging
        )
        return response.choices[0].message.content
    except openai.error.OpenAIError as e:
        st.error(f"OpenAI API Error: {e}")
        return f"Sorry, I encountered an error: {e}"
    except Exception as e:
        st.error(f"An unexpected error occurred: {e}")
        return "Sorry, I encountered an unexpected error."



def initialize_session_state():
    """Initializes session state variables."""
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    if "display_messages" not in st.session_state:
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}] # Separate list for displayed messages
    if "model" not in st.session_state:
        st.session_state.model = DEFAULT_MODEL


# --- Main App ---
def main():
    initialize_session_state()

    # --- Sidebar ---
    st.sidebar.header("Settings")
    model = st.sidebar.selectbox("Model", ["gpt-4o", "gpt-3.5-turbo"], index=0)  # Add model selection
    st.session_state.model = model #update the model
    clear_history = st.sidebar.button("Clear Chat History")
    if clear_history:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # Clear displayed messages too
        st.rerun()  # Force a rerun to update the display

    st.title("🧠 GPT-4o Chatbot with Memory") #moved title

    # Display previous messages
    for message in st.session_state.display_messages:
        if message["role"] != "system":  # Don't display the system prompt
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    # User input
    user_input = st.chat_input("You:", key="user_input")  # Use key for chat_input


    if user_input:
        st.session_state.messages.append({"role": "user", "content": user_input})
        st.session_state.display_messages.append({"role": "user", "content": user_input}) # Also add to display list

        # Get assistant reply
        reply = get_openai_response(st.session_state.messages, model=st.session_state.model)
        st.session_state.messages.append({"role": "assistant", "content": reply})
        st.session_state.display_messages.append({"role": "assistant", "content": reply}) # Also add to display list

        with st.chat_message("assistant"):
            st.markdown(reply)

        # Keep only the last MAX_CONTEXT_MESSAGES messages (including system prompt)
        st.session_state.messages = st.session_state.messages[:1] + st.session_state.messages[-MAX_CONTEXT_MESSAGES + 1:]
        st.rerun() # Force a rerun to update the display



if __name__ == "__main__":
    main()

Let's break down this Streamlit chatbot code:

1. Imports and Setup

  • Imports essential libraries (streamlit, openai, os, dotenv) for chat functionality
  • Loads environment variables and sets up OpenAI API key

2. Configuration

  • Defines system prompt for the chatbot's personality
  • Sets default model (gpt-4o) and maximum context messages (10)

3. Helper Functions

  • get_openai_response(): Handles API communication with OpenAI
    • Includes error handling for API issues
    • Sets 60-second timeout
    • Returns either the response or error message
  • initialize_session_state(): Sets up Streamlit's session state
    • Creates message history for API calls
    • Maintains separate display messages
    • Initializes model selection

4. Main Application

  • Sidebar with settings:
    • Model selection dropdown
    • Clear chat history button
  • Chat interface:
    • Displays message history
    • Handles user input
    • Shows bot responses
    • Maintains conversation context within message limit

5. Key Features

  • Memory management through session state
  • Error handling for API calls
  • Message history limitation for performance
  • Real-time chat updates using Streamlit's chat components

This example provides an user-friendly chatbot with better error handling, a system prompt, and a limit on the conversation history.  It also allows the user to select the model.

4.1 Flask and Streamlit Implementations

In this comprehensive chapter, you'll learn to build a sophisticated yet beginner-friendly chatbot that demonstrates advanced memory capabilities. Unlike simple chatbots that only respond to the last message, this implementation will maintain context throughout entire conversations, allowing for more natural and contextually aware interactions. You'll work with two powerful Python frameworks for web applications: Flask, a flexible micro-framework that gives you complete control over your application, and Streamlit, a modern framework designed for rapid application development.

The chapter provides detailed, step-by-step guidance for both frameworks, allowing you to choose the approach that best suits your needs. Whether you select Flask's granular control or Streamlit's streamlined development process, you'll learn how to integrate your chatbot with OpenAI's GPT-4o model, leveraging its advanced natural language processing capabilities for intelligent conversations.

Upon completing this chapter, you'll have created a fully functional chatbot with these essential features:

  • Runs in a local web app - Your chatbot will operate through a web interface that you can access from any browser on your local machine
  • Handles user input in real time - The application will process and respond to user messages immediately, creating a smooth conversation flow
  • Maintains multi-turn conversation memory - Your chatbot will remember previous exchanges and use this context to provide more relevant and coherent responses
  • Uses OpenAI's Chat Completions API to craft responses - You'll learn how to integrate with OpenAI's powerful API to generate human-like responses based on conversation context

We'll begin with 11.1 Flask and Streamlit Implementations, providing an in-depth exploration of both frameworks and guiding you through the setup process for each approach. You'll learn the specific advantages and considerations for both implementations, enabling you to make an informed choice for your project.

Let's dive deep into these powerful web development tools to understand their architectures, capabilities, and use cases. Each framework offers unique advantages for building web applications, and understanding their characteristics will help you make an informed decision for your chatbot implementation:

Flask is a lightweight and versatile micro web framework for Python. It's called "micro" because it provides just the essential components you need to build a web application, giving you complete control over your project's architecture. Key features include:

  • Routing system for handling HTTP requests
  • Template engine for dynamic HTML generation
  • Built-in development server
  • Extensive documentation and large community support
  • Easy integration with various extensions

Streamlit is a modern, Python-based framework designed specifically for creating data applications with minimal effort. It transforms Python scripts into shareable web applications through simple API calls. Notable features include:

  • Built-in widgets for interactive data visualization
  • Automatic hot-reloading during development
  • Native support for machine learning frameworks
  • Simple deployment process
  • Real-time user interaction handling

Both frameworks excel in different scenarios — Flask offers granular control and flexibility for custom web applications, while Streamlit specializes in rapid prototyping and data-focused applications. You're welcome to explore either or both approaches as we proceed with implementation. Let's walk through a detailed implementation of each to help you understand their unique strengths.

Choosing Between Flask and Streamlit

Consider trying both frameworks to gain valuable experience. Flask will give you deeper insights into web development concepts like routing, templates, and HTTP requests. Meanwhile, Streamlit excels at quick prototyping and is perfect when you need to demonstrate functionality rapidly without spending time on frontend development.

4.1.1 Choosing Between Flask and Streamlit: A Detailed Comparison

Let's start with a comprehensive exploration of the key considerations when choosing between Flask and Streamlit for your chatbot project. Both frameworks offer distinct advantages and potential trade-offs that can significantly impact your development experience and final application. Understanding these differences will help you make an informed decision that aligns with your project goals, technical requirements, and development timeline.

Flask and Streamlit represent two different philosophical approaches to web application development. Flask embodies the microframework concept, providing developers with complete control and flexibility, while Streamlit emphasizes rapid development and immediate results through its streamlined, opinionated approach. The choice between them often depends on factors such as project complexity, required customization, deployment requirements, and your team's technical expertise.

When deciding between Flask and Streamlit for your chatbot project, let's explore these key factors in detail:

  • Flask requires more initial setup but offers greater control:
    • You'll need to create and configure routes to handle different URL endpoints
    • Setting up HTML templates requires understanding of frontend development
    • Manual handling of HTTP requests gives you fine-grained control over data flow
    • Session management and user authentication need custom implementation
    • This level of control is powerful but requires deeper web development expertise and more development time
  • Streamlit enables rapid prototyping with minimal configuration:
    • Simple Python functions can create interactive web elements instantly
    • Built-in state management handles data persistence automatically
    • No need to understand HTML, CSS, or JavaScript
    • Interactive widgets are available out-of-the-box
    • Perfect for quick prototypes and data-focused applications where speed of development is crucial
  • Application Requirements
    • Choose Flask for complex, custom web applications:
      • Perfect for projects requiring detailed customization of user interface and backend logic
      • Ideal when you need granular control over authentication systems and user management
      • Excellent for applications with complex routing requirements and multiple endpoints
      • Best choice when your app needs to integrate with various external services and APIs
      • Suitable for projects that may need to scale significantly in the future
      • Recommended when you have experienced developers who understand web development principles
    • Pick Streamlit for data-focused apps needing quick deployment:
      • Excellent for data visualization projects and analytics dashboards
      • Perfect for rapid prototyping of machine learning model interfaces
      • Ideal for projects where data scientists need to quickly demonstrate results
      • Great for applications that primarily focus on data presentation and interaction
      • Suitable for teams that want to minimize frontend development overhead
      • Best when you need to quickly iterate on features based on user feedback
  • Flask provides complete frontend customization freedom:
    • Design fully custom interfaces using HTML, CSS, and JavaScript
    • Create pixel-perfect layouts and brand-specific designs
    • Implement complex interactions and animations
    • Build responsive designs that work across all devices
    • Add custom JavaScript libraries and frameworks as needed
    • Create unique user experiences without limitations
  • Streamlit offers pre-built components ideal for data visualization:
    • Rich set of ready-to-use widgets (charts, graphs, tables)
    • Interactive elements like sliders, buttons, and input fields
    • Real-time data updates and visualizations
    • Built-in support for popular data visualization libraries
    • Simple API for creating complex interfaces without frontend expertise
    • Rapid prototyping capabilities for data-driven applications
  • Long-term Scalability and System Architecture
    • Flask's flexibility enables robust scalability:
      • Modular architecture allows adding new features without disrupting existing code
      • Supports horizontal scaling across multiple servers for increased capacity
      • Easy integration with load balancers and microservices
      • Compatible with various database systems (SQL, NoSQL, distributed databases)
      • Allows implementation of complex caching strategies
      • Supports custom middleware for performance optimization
      • Enables sophisticated authentication and authorization systems
    • Streamlit's architecture has specific limitations:
      • Built primarily for single-page applications and data dashboards
      • May face performance challenges with large concurrent user bases
      • Limited options for complex routing and URL management
      • Restricted ability to implement complex state management
      • Can become harder to maintain as business logic grows
      • Less suitable for microservices architecture
      • May require complete rewrite when scaling beyond certain complexity

4.1.2 Flask

Flask is a powerful and versatile micro web framework for Python that has revolutionized web development with its elegant simplicity and remarkable flexibility. When we call it a "micro" framework, this doesn't mean it's limited in capabilities - rather, it follows a minimalist philosophy where developers start with a lean core and build up exactly what they need. This thoughtful approach eliminates unnecessary complexity while giving developers unprecedented control over their application's architecture. Flask empowers developers to create everything from simple APIs to complex web applications, all while maintaining clean, maintainable code.

Let's dive deep into Flask's key features and understand why they make it such a powerful choice:

  • Routing system for handling HTTP requests - Flask's sophisticated routing mechanism goes beyond simple URL mapping. It provides a decorator-based syntax that makes creating RESTful APIs intuitive, supports dynamic URL patterns with variable rules, and allows for complex URL hierarchies. This flexibility enables developers to structure their applications logically while maintaining clean URL schemes.
  • Template engine for dynamic HTML generation - The integration with Jinja2, one of Python's most powerful templating engines, offers far more than basic HTML generation. It provides template inheritance for DRY (Don't Repeat Yourself) code, custom filters for data manipulation, context processors for global variables, and macro support for reusable HTML components. These features make it possible to create sophisticated, maintainable frontend code without sacrificing flexibility.
  • Built-in development server - Flask's development server is a sophisticated tool that goes beyond basic HTTP serving. It includes features like automatic reloading when code changes, detailed debug pages with interactive stack traces, and the ability to handle multiple concurrent requests. While perfect for development, Flask seamlessly transitions to production-grade servers like Gunicorn or uWSGI when you're ready to deploy.
  • Extensive documentation and large community support - Flask's documentation is not just comprehensive - it's a masterclass in Python web development. Each feature is thoroughly explained with practical examples, best practices, and security considerations. The vibrant community contributes countless extensions, tutorials, and solutions to common problems, making Flask one of the most well-supported frameworks in the Python ecosystem.
  • Easy integration with various extensions - Flask's extension system is a testament to its excellent design. Through a consistent API, extensions can add sophisticated features like database ORM support (SQLAlchemy), form validation (WTForms), user authentication (Flask-Login), API documentation (Swagger/OpenAPI), and much more. These extensions maintain Flask's lightweight nature while providing enterprise-grade functionality when needed.

4.1.3 Flask Chatbot Implementation

This example implements a simple chatbot using Flask and OpenAI's API. The chatbot takes user input via a web form, sends it to OpenAI's GPT-4o model, and displays the model's response in the browser.  It also maintains a basic conversation history.

Step 1: Install Dependencies

pip install flask openai python-dotenv
  • flask: A web framework for building the chatbot application.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Flask App (app.py)

from flask import Flask, request, render_template, session
import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

app = Flask(__name__)
app.secret_key = os.urandom(24)  # Required for session

@app.route("/", methods=["GET", "POST"])
def chat():
    # Use session to store conversation history
    if "conversation_history" not in session:
        session["conversation_history"] = []

    conversation_history = session["conversation_history"]

    if request.method == "POST":
        user_input = request.form["user_input"]
        conversation_history.append({"role": "user", "content": user_input})

        try:
            response = openai.ChatCompletion.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."}
                ] + conversation_history
            )
            assistant_reply = response.choices[0].message.content
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history #save the updated history

        except openai.error.OpenAIError as e:
            assistant_reply = f"Error: {e}"
            conversation_history.append({"role": "assistant", "content": assistant_reply})
            session["conversation_history"] = conversation_history

        return render_template("chat.html", reply=assistant_reply)

    else:
        # Reset the history.  Important for starting a new chat.
        session["conversation_history"] = []
        assistant_reply = "" # Initialize
        return render_template("chat.html", reply=assistant_reply)

if __name__ == "__main__":
    app.run(debug=True)

Code breakdown

  • from flask import ...: Imports necessary modules from Flask.
    • Flask: The Flask class to create the web application.
    • request: To access incoming request data (form data).
    • render_template: To render HTML templates.
    • session: To manage user sessions. Conversation history is now stored in the session.
  • import openai: Imports the OpenAI library.
  • import os: Imports the os module for environment variables.
  • from dotenv import load_dotenv: Imports load_dotenv to load variables from a .env file.
  • load_dotenv(): Loads the environment variables from a .env file.
  • openai.api_key = os.getenv("OPENAI_API_KEY"): Retrieves the OpenAI API key from the environment variable OPENAI_API_KEY and sets it for the OpenAI library.
  • app = Flask(__name__): Creates a Flask application instance.
  • app.secret_key = os.urandom(24)Important: Sets a secret key for the Flask application. This is required to use sessions. os.urandom(24) generates a random, cryptographically secure key.
  • @app.route("/", methods=["GET", "POST"]): This decorator defines a route for the root URL ("/"). The chat() function will handle both GET and POST requests to this URL.
  • def chat():: The function that handles requests to the root URL.
  • if "conversation_history" not in session: session["conversation_history"] = []: This is crucial for persisting the conversation history across requests. The conversation history is stored as a list of dictionaries in the user's session. If conversation_history is not in the session, it initializes it as an empty list.
  • conversation_history = session["conversation_history"]: Gets the conversation history from the session.
  • if request.method == "POST":: Handles POST requests, which occur when the user submits the form.
    • user_input = request.form["user_input"]: Gets the user's input from the form data. The input field in the HTML template is named "user_input".
    • conversation_history.append({"role": "user", "content": user_input}): Appends the user's input to the conversation history.
    • try...except openai.error.OpenAIError as e: Includes error handling for OpenAI API errors.
    • response = openai.ChatCompletion.create(...): Calls the OpenAI API to get a response from the GPT-4o model.
      • model: Specifies the language model to use ("gpt-4o").
      • messages: A list of message dictionaries representing the conversation history. The system message is included at the beginning, followed by the conversation history.
    • assistant_reply = response.choices[0].message.content: Extracts the assistant's reply from the API response.
    • conversation_history.append({"role": "assistant", "content": assistant_reply}): Appends the assistant's reply to the conversation history.
    • session["conversation_history"] = conversation_historyCrucial: Updates the conversation_history in the session, so it's preserved for the next turn of the conversation.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template and passes the assistant's reply to the template.
  • else:: Handles GET requests, which occur when the user initially loads the page.
    • session["conversation_history"] = []: Reset the history when the user loads the page.
    • assistant_reply = "": Initializes assistant_reply to an empty string.
    • return render_template("chat.html", reply=assistant_reply): Renders the "chat.html" template with an empty reply.
  • if __name__ == "__main__":: This ensures that the Flask development server is started only when the script is executed directly (not when imported as a module).
  • app.run(debug=True): Starts the Flask development server in debug mode. Debug mode provides helpful error messages and automatic reloading when you make changes to the code.

Step 3: Create a Simple HTML Template (templates/chat.html)

<!DOCTYPE html>
<html>
<head><title>Flask Chatbot</title></head>
<body>
  <h2>GPT-4o Chatbot</h2>
  <form method="post">
    <textarea name="user_input" rows="4" cols="50"></textarea><br>
    <input type="submit" value="Send">
  </form>
  <hr>
  <p><strong>Assistant:</strong> {{ reply }}</p>
</body>
</html>

Code breakdown:

  • <!DOCTYPE html>: Declares the document type as HTML5.
  • <html>: The root element of the HTML document.
  • <head>: Contains metadata about the HTML document.
    • <title>: Specifies the title of the HTML page, which is shown in the browser's title bar or tab.
  • <body>: Contains the visible content of the HTML page.
    • <h2>: Defines a level 2 heading.
    • <form method="post">: Defines an HTML form that submits data using the POST method.
      • <textarea>: A multi-line text input field where the user can enter their query. The name attribute is set to "user_input", which is used to access the input in the Flask code.
      • <input type="submit" value="Send">: A submit button that sends the form data to the server.
    • <hr>: A horizontal rule, used to separate content.
    • <p>: A paragraph element to display the assistant's reply.
      • <strong>: Defines strong text (usually displayed in bold).
      • {{ reply }}: A Jinja template variable that will be replaced with the value of the reply variable passed from the Flask code.
  • templates: Flask, by default, looks for HTML templates in a folder named "templates" in the same directory as your app.py file. So, this file should be saved as templates/chat.html.

This example creates a robust, functional chatbot application. To implement it, create a templates folder in the same directory as your app.py file, save the HTML template as chat.html inside that folder, and set the OPENAI_API_KEY environment variable.

4.1.4 Streamlit

Streamlit represents a revolutionary advancement in data application development. This Python-based framework has fundamentally transformed how developers approach web application creation. At its core, Streamlit embodies a philosophy of simplicity and efficiency, enabling developers to convert standard Python scripts into sophisticated web applications with minimal code overhead.

What sets it apart from conventional web frameworks is its innovative approach - while traditional frameworks demand extensive expertise in HTML, CSS, and JavaScript, Streamlit introduces a paradigm shift by providing an abstraction layer through its intuitive API calls. This design choice has made it an invaluable tool, particularly for data scientists and machine learning engineers who can now focus on their core competencies while creating professional-grade interactive demonstrations.

Let's explore its comprehensive feature set in detail:

  • Built-in widgets for interactive data visualization - Streamlit's extensive component library includes sophisticated charts, interactive graphs, customizable sliders, and versatile input fields. These pre-built components eliminate the need for frontend development expertise, allowing creators to focus on data presentation rather than web development intricacies. The framework handles all the complex rendering and state management behind the scenes.
  • Automatic hot-reloading during development - Streamlit's development environment features an intelligent file watching system that monitors your source code for changes. When modifications are detected, it automatically refreshes the web application, creating a seamless development experience. This feature significantly reduces development time by eliminating the traditional save-refresh cycle common in web development.
  • Native support for machine learning frameworks - The framework's deep integration with popular machine learning libraries such as TensorFlow, PyTorch, and scikit-learn goes beyond simple compatibility. It provides specialized components and optimizations for ML workflows, making it an ideal platform for creating interactive model demonstrations, real-time prediction interfaces, and educational tools for machine learning concepts.
  • Simple deployment process - Streamlit's deployment infrastructure has been carefully designed to minimize operational complexity. Whether using Streamlit's own cloud platform or alternative hosting services, the deployment process has been streamlined to require minimal configuration. This approach ensures that developers can quickly share their applications with stakeholders without getting bogged down in deployment technicalities.
  • Real-time user interaction handling - The framework incorporates sophisticated state management and caching mechanisms that operate seamlessly in the background. These systems ensure optimal performance during complex user interactions, managing data flow and component updates efficiently without requiring developers to implement custom backend logic. This results in responsive applications that can handle complex computational tasks while maintaining smooth user experiences.

4.1.5 Streamlit Chatbot Implementation

This example creates a chatbot using Streamlit and OpenAI's API. It's a simple application that allows users to interact with the GPT-4o model through a chat interface. The application maintains chat history using Streamlit's session state. This enhanced version includes error handling, a system prompt, and a sidebar for additional settings.

  • *Step 1: Install Dependencies
pip install streamlit openai python-dotenv

Code breakdown:

  • streamlit: A Python library for creating interactive web applications, in this case, the chatbot interface.
  • openai: The OpenAI Python library for interacting with the GPT-4o model.
  • python-dotenv: A library to load environment variables from a .env file.

Step 2: Create the Streamlit App (chatbot.py)

import streamlit as st
import openai
import os
from dotenv import load_dotenv
import time  # For handling potential API errors


load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")


# --- Configuration ---
SYSTEM_PROMPT = "You are a helpful, friendly, and knowledgeable assistant.  Answer all questions clearly and concisely."
DEFAULT_MODEL = "gpt-4o"
MAX_CONTEXT_MESSAGES = 10  # Limit conversation history


# --- Helper Functions ---
def get_openai_response(messages, model=DEFAULT_MODEL):
    """
    Sends a message to OpenAI's Chat API and handles potential errors.
    Args:
        messages: A list of message dictionaries.
        model: The OpenAI model to use.  Defaults to gpt-4o
    Returns:
        The assistant's reply, or an error message.
    """
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            timeout=60,  # Add a timeout to prevent hanging
        )
        return response.choices[0].message.content
    except openai.error.OpenAIError as e:
        st.error(f"OpenAI API Error: {e}")
        return f"Sorry, I encountered an error: {e}"
    except Exception as e:
        st.error(f"An unexpected error occurred: {e}")
        return "Sorry, I encountered an unexpected error."



def initialize_session_state():
    """Initializes session state variables."""
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    if "display_messages" not in st.session_state:
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}] # Separate list for displayed messages
    if "model" not in st.session_state:
        st.session_state.model = DEFAULT_MODEL


# --- Main App ---
def main():
    initialize_session_state()

    # --- Sidebar ---
    st.sidebar.header("Settings")
    model = st.sidebar.selectbox("Model", ["gpt-4o", "gpt-3.5-turbo"], index=0)  # Add model selection
    st.session_state.model = model #update the model
    clear_history = st.sidebar.button("Clear Chat History")
    if clear_history:
        st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
        st.session_state.display_messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # Clear displayed messages too
        st.rerun()  # Force a rerun to update the display

    st.title("🧠 GPT-4o Chatbot with Memory") #moved title

    # Display previous messages
    for message in st.session_state.display_messages:
        if message["role"] != "system":  # Don't display the system prompt
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    # User input
    user_input = st.chat_input("You:", key="user_input")  # Use key for chat_input


    if user_input:
        st.session_state.messages.append({"role": "user", "content": user_input})
        st.session_state.display_messages.append({"role": "user", "content": user_input}) # Also add to display list

        # Get assistant reply
        reply = get_openai_response(st.session_state.messages, model=st.session_state.model)
        st.session_state.messages.append({"role": "assistant", "content": reply})
        st.session_state.display_messages.append({"role": "assistant", "content": reply}) # Also add to display list

        with st.chat_message("assistant"):
            st.markdown(reply)

        # Keep only the last MAX_CONTEXT_MESSAGES messages (including system prompt)
        st.session_state.messages = st.session_state.messages[:1] + st.session_state.messages[-MAX_CONTEXT_MESSAGES + 1:]
        st.rerun() # Force a rerun to update the display



if __name__ == "__main__":
    main()

Let's break down this Streamlit chatbot code:

1. Imports and Setup

  • Imports essential libraries (streamlit, openai, os, dotenv) for chat functionality
  • Loads environment variables and sets up OpenAI API key

2. Configuration

  • Defines system prompt for the chatbot's personality
  • Sets default model (gpt-4o) and maximum context messages (10)

3. Helper Functions

  • get_openai_response(): Handles API communication with OpenAI
    • Includes error handling for API issues
    • Sets 60-second timeout
    • Returns either the response or error message
  • initialize_session_state(): Sets up Streamlit's session state
    • Creates message history for API calls
    • Maintains separate display messages
    • Initializes model selection

4. Main Application

  • Sidebar with settings:
    • Model selection dropdown
    • Clear chat history button
  • Chat interface:
    • Displays message history
    • Handles user input
    • Shows bot responses
    • Maintains conversation context within message limit

5. Key Features

  • Memory management through session state
  • Error handling for API calls
  • Message history limitation for performance
  • Real-time chat updates using Streamlit's chat components

This example provides an user-friendly chatbot with better error handling, a system prompt, and a limit on the conversation history.  It also allows the user to select the model.