> ## Documentation Index
> Fetch the complete documentation index at: https://genai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 7. Custom ChatGPT Application

> Build a persistent custom ChatGPT clone using Streamlit, SQLite database, and user logins

In this section, you will build a complete, stateful **Custom ChatGPT Clone** web application. This extends the Streamlit chatbot by adding user session authentication, local SQLite persistence, and chat log retrieval.

## Objectives

1. Implement a user login system using Streamlit inputs.
2. Link the active username to SQL-backed session storage (`SQLChatMessageHistory`).
3. Render user-specific history dynamically on login.
4. Add sidebar administrative controls (like clearing chat logs).

## Plan

1. **User Authentication**: Setup a sidebar input to accept a username. If no user is logged in, show an info prompt to block the interface.
2. **SQLite Connection**: Once a username is entered, initialize `SQLChatMessageHistory` using the username as the session identifier.
3. **History Rendering**: Retrieve and loop through past session messages from the database, displaying them in user/assistant bubbles.
4. **Chat Logic**: Integrate chat inputs so new messages are immediately written to the local database, passed to the model (initialized via core abstractions), and the resulting AI response is saved back to SQLite.
5. **Clear Controls**: Implement a sidebar button that executes `chat_history.clear()` to erase logs and resets the interface.

## Step-by-Step Implementation

Let's build the Custom ChatGPT Clone incrementally step-by-step:

#### Step 1: Imports and Page Config

**Plan:**

1. Import `streamlit`, `SQLChatMessageHistory`, and message schemas.
2. Load environment variables.
3. Configure the browser page title and icon using `st.set_page_config`.

**Code Implementation:**

```python theme={null}
import streamlit as st
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain.chat_models import init_chat_model
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from dotenv import load_dotenv

load_dotenv()

st.set_page_config(page_title="Custom ChatGPT App", page_icon="🤖")

st.title("🤖 Custom ChatGPT Application")
st.write("A secure chatbot interface featuring local SQLite history persistence.")
```

#### Step 2: Sidebar Authentication

**Plan:**

1. Create a text input box in the sidebar for the username.
2. Use an `if username:` check to block execution of the chatbot until a valid session ID is provided.

**Code Implementation:**

```python theme={null}
# Sidebar Login Interface
st.sidebar.header("🔐 User Authentication")
username = st.sidebar.text_input("Enter Username to Log In:", placeholder="e.g., satish")

if username:
    st.sidebar.success(f"Logged in as: **{username}**")
    # Chatbot logic will be nested inside this block
else:
    st.info("👈 Please enter a username in the sidebar to log in and load your conversation history.")
```

#### Step 3: Initialize SQLite Persistent History & Model

**Plan:**

1. Setup connection variables and instantiate `SQLChatMessageHistory` inside the authentication block.
2. Add a default `SystemMessage` if the session history is brand new.
3. Initialize the chat model using `init_chat_model`.

**Code Implementation:**

```python theme={null}
# Setup SQLite persistent session unique to the logged-in user
DB_CONNECTION = "sqlite:///chat_history.db"
chat_history = SQLChatMessageHistory(
    session_id=username,
    connection_string=DB_CONNECTION
)

# Initialize Chat Model using core abstractions (Groq Llama model)
model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

# If the SQL history is empty, write a default SystemMessage
if len(chat_history.messages) == 0:
    chat_history.add_message(SystemMessage(content="You are a helpful and custom ChatGPT assistant."))
```

#### Step 4: Render Saved Logs, Input Loops, and Clear Controls

**Plan:**

1. Loop through `chat_history.messages` to render user and assistant message bubbles.
2. Collect chat inputs, save them to the database, invoke the model, render the AI's reply, and save it.
3. Add a sidebar clear button that invokes `.clear()` on the database history.

**Code Implementation:**

```python theme={null}
# Sidebar Actions
if st.sidebar.button("🗑️ Clear Chat History"):
    chat_history.clear()
    st.sidebar.warning("History cleared!")
    st.rerun()

# Render persisted chat history from SQLite
for message in chat_history.messages:
    if isinstance(message, HumanMessage):
        with st.chat_message("user"):
            st.markdown(message.content)
    elif isinstance(message, AIMessage):
        with st.chat_message("assistant"):
            st.markdown(message.content)

# User chat input
if prompt := st.chat_input("Ask ChatGPT anything..."):
    # Render user message
    with st.chat_message("user"):
        st.markdown(prompt)
    # Save to SQLite database
    chat_history.add_user_message(prompt)

    # Get AI response
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        with st.spinner("Responding..."):
            response = model.invoke(chat_history.messages)
            message_placeholder.markdown(response.content)
    
    # Save response to SQLite database
    chat_history.add_ai_message(response.content)
```

## Combined Code

Combining all the steps above gives the final complete script:

```python theme={null}
import streamlit as st
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain.chat_models import init_chat_model
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from dotenv import load_dotenv

load_dotenv()

st.set_page_config(page_title="Custom ChatGPT App", page_icon="🤖")

st.title("🤖 Custom ChatGPT Application")
st.write("A secure chatbot interface featuring local SQLite history persistence.")

# Sidebar Login Interface
st.sidebar.header("🔐 User Authentication")
username = st.sidebar.text_input("Enter Username to Log In:", placeholder="e.g., satish")

if username:
    st.sidebar.success(f"Logged in as: **{username}**")
    
    # Setup SQLite persistent session unique to the logged-in user
    DB_CONNECTION = "sqlite:///chat_history.db"
    chat_history = SQLChatMessageHistory(
        session_id=username,
        connection_string=DB_CONNECTION
    )

    # Sidebar Actions
    if st.sidebar.button("🗑️ Clear Chat History"):
        chat_history.clear()
        st.sidebar.warning("History cleared!")
        st.rerun()

    # Initialize model using core abstractions (Groq Llama model)
    model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

    # If the SQL history is empty, write a default SystemMessage
    if len(chat_history.messages) == 0:
        chat_history.add_message(SystemMessage(content="You are a helpful and custom ChatGPT assistant."))

    # Render persisted chat history from SQLite
    for message in chat_history.messages:
        if isinstance(message, HumanMessage):
            with st.chat_message("user"):
                st.markdown(message.content)
        elif isinstance(message, AIMessage):
            with st.chat_message("assistant"):
                st.markdown(message.content)

    # User chat input
    if prompt := st.chat_input("Ask ChatGPT anything..."):
        # Render user message
        with st.chat_message("user"):
            st.markdown(prompt)
        # Save to SQLite database
        chat_history.add_user_message(prompt)

        # Get AI response
        with st.chat_message("assistant"):
            message_placeholder = st.empty()
            with st.spinner("Responding..."):
                response = model.invoke(chat_history.messages)
                message_placeholder.markdown(response.content)
        
        # Save response to SQLite database
        chat_history.add_ai_message(response.content)
else:
    st.info("👈 Please enter a username in the sidebar to log in and load your conversation history.")
```

## Exercise: Dynamic Model Swapper 🔀

#### Goal

Extend the sidebar options to include a model selector selectbox (`st.sidebar.selectbox`) that allows the logged-in user to swap between Llama (`llama-3.3-70b-versatile` via Groq) and Gemini (`gemini-2.5-flash` via Google GenAI) models dynamically without resetting the conversation history.

#### Plan

1. Add a selectbox in the sidebar containing model strings: `"Llama 3.3 (Groq)"` and `"Gemini 2.5 (Google)"`.
2. Based on selection, set the corresponding `model_name` and `model_provider` parameters.
3. Pass these parameters to the `init_chat_model` instantiation inside the app execution.

<Accordion title="Solution">
  ```python theme={null}
  import streamlit as st
  from langchain_community.chat_message_histories import SQLChatMessageHistory
  from langchain.chat_models import init_chat_model
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
  from dotenv import load_dotenv

  load_dotenv()

  st.set_page_config(page_title="Custom ChatGPT App", page_icon="🤖")

  st.title("🤖 Custom ChatGPT Application")

  # Sidebar Authentication
  st.sidebar.header("🔐 User Authentication")
  username = st.sidebar.text_input("Enter Username:", placeholder="e.g., satish")

  if username:
      st.sidebar.success(f"Logged in as: **{username}**")
      
      # Model Selection
      selected_model = st.sidebar.selectbox(
          "Select Model Provider:",
          ["Llama 3.3 (Groq)", "Gemini 2.5 (Google)"]
      )
      
      if selected_model == "Llama 3.3 (Groq)":
          model_name, provider = "llama-3.3-70b-versatile", "groq"
      else:
          model_name, provider = "gemini-2.5-flash", "google_genai"

      # Setup SQL History
      chat_history = SQLChatMessageHistory(
          session_id=username,
          connection_string="sqlite:///chat_history.db"
      )

      if st.sidebar.button("🗑️ Clear History"):
          chat_history.clear()
          st.sidebar.warning("History cleared!")
          st.rerun()

      # Dynamic Model loading using core abstractions
      model = init_chat_model(model_name, model_provider=provider)

      if len(chat_history.messages) == 0:
          chat_history.add_message(SystemMessage(content="You are a helpful chatbot."))

      # Render History
      for message in chat_history.messages:
          if isinstance(message, HumanMessage):
              with st.chat_message("user"):
                  st.markdown(message.content)
          elif isinstance(message, AIMessage):
              with st.chat_message("assistant"):
                  st.markdown(message.content)

      # Chat input
      if prompt := st.chat_input("Ask anything..."):
          with st.chat_message("user"):
              st.markdown(prompt)
          chat_history.add_user_message(prompt)

          with st.chat_message("assistant"):
              message_placeholder = st.empty()
              with st.spinner("Responding..."):
                  response = model.invoke(chat_history.messages)
                  message_placeholder.markdown(response.content)
          chat_history.add_ai_message(response.content)
  else:
      st.info("👈 Log in to get started.")
  ```
</Accordion>

## Practice & Exercises

To execute the Custom ChatGPT app locally, run the script from your terminal:

```bash theme={null}
streamlit run langchain/1_chat_models/7_chat_model_custom_chatgpt.py
```
