> ## 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.

# 6. Streamlit Chatbot App

> Build a web-based chatbot user interface using Streamlit and LangChain core abstractions

In this section, you will learn how to wrap your conversational chat loop in a fully functional web-based user interface using **Streamlit** and LangChain core abstractions.

## Objectives

1. Configure session state memory to retain message history across page reruns.
2. Render user and assistant chat message containers.
3. Hook Streamlit chat inputs to LangChain's invocation pipeline.

## Streamlit Chatbot App

#### Goal

Build a graphical web application where users can type messages and receive chat replies from a Groq-provided Llama model in real-time.

#### User Interface Layout

1. **Title Banner**: Displays a header "💬 LangChain Streamlit Chatbot".
2. **Chat Window**: Shows user queries in a chat box on the right, and AI assistant answers on the left.
3. **Chat Input Field**: Pinned to the bottom of the screen for user inputs.

#### Plan

1. Import `streamlit`, `init_chat_model`, and message schemas.
2. Initialize the chat model using `init_chat_model("llama-3.3-70b-versatile", model_provider="groq")`.
3. Check and initialize `st.session_state.messages` list with a default `SystemMessage`.
4. Loop through existing session messages to render them in containers using `st.chat_message`.
5. Capture user inputs using `st.chat_input`, append to history, invoke the model, render the response, and append the response back to history.

### Code Implementation

Let's build the Streamlit application incrementally step-by-step:

#### Step 1: Imports and Setup

We start by importing `streamlit`, the unified `init_chat_model` initializer, and message schemas. We then load environment variables:

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

load_dotenv()
```

#### Step 2: Initialize Model and Message State

We set up page headers and check if our model and chat history list exist in `st.session_state`. This ensures our state objects persist across page reruns:

```python theme={null}
st.title("💬 LangChain Streamlit Chatbot")
st.write("A simple chatbot interface powered by LangChain core abstractions.")

# Initialize the chat model using core abstractions (Groq Llama model)
if "model" not in st.session_state:
    st.session_state.model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

# Initialize session state for message history
if "messages" not in st.session_state:
    st.session_state.messages = [
        SystemMessage(content="You are a helpful and polite AI chatbot assistant.")
    ]
```

#### Step 3: Render Message History

We loop through `st.session_state.messages` and render human queries and AI answers inside Streamlit's native bubble containers (`st.chat_message`). We skip rendering the `SystemMessage`:

```python theme={null}
# Display past chat messages (skipping SystemMessage)
for message in st.session_state.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)
```

#### Step 4: Handle Chat Input and Response

We query a text box using `st.chat_input`. When the user enters a prompt, we render it, append it to `st.session_state.messages`, query the model with the entire history, display the response inside a spinner, and append the reply to history.

> \[!NOTE]
> **The Walrus Operator (`:=`)**:
> The syntax `if prompt := st.chat_input("What is on your mind?"):` uses Python's assignment expression (walrus operator) to:
>
> * **Assign** the user's input string returned by `st.chat_input` directly to the `prompt` variable.
> * **Evaluate** the condition; if the input is not empty/submitted, the condition resolves to `True` and executes the code block. If no input is submitted, it resolves to `False` and skips execution.

```python theme={null}
# Accept user input
if prompt := st.chat_input("What is on your mind?"):
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)
    
    # Add user message to session state history
    st.session_state.messages.append(HumanMessage(content=prompt))
    
    # Get AI response
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        with st.spinner("Thinking..."):
            response = st.session_state.model.invoke(st.session_state.messages)
            message_placeholder.markdown(response.content)
            
    # Add AI response to session state history
    st.session_state.messages.append(AIMessage(content=response.content))
```

#### Step 5: Complete Application Code

Combining all the steps above gives the final completed script:

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

load_dotenv()

st.title("💬 LangChain Streamlit Chatbot")
st.write("A simple chatbot interface powered by LangChain core abstractions.")

# Initialize the chat model using core abstractions (Groq Llama model)
if "model" not in st.session_state:
    st.session_state.model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

# Initialize session state for message history
if "messages" not in st.session_state:
    st.session_state.messages = [
        SystemMessage(content="You are a helpful and polite AI chatbot assistant.")
    ]

# Display past chat messages (skipping SystemMessage)
for message in st.session_state.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)

# Accept user input
if prompt := st.chat_input("What is on your mind?"):
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)
    
    # Add user message to session state history
    st.session_state.messages.append(HumanMessage(content=prompt))
    
    # Get AI response
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        with st.spinner("Thinking..."):
            response = st.session_state.model.invoke(st.session_state.messages)
            message_placeholder.markdown(response.content)
            
    # Add AI response to session state history
    st.session_state.messages.append(AIMessage(content=response.content))
```

## Exercise: Custom System Prompt Selector 🎨

#### Goal

Add a sidebar dropdown (`st.sidebar.selectbox`) that allows the user to select the chatbot's persona (e.g., "Math Tutor", "French Translator", "Creative Writer") and updates the initial `SystemMessage` dynamically.

#### Sample Input

Sidebar selection: `"French Translator"`

#### Sample Output

Assistant greets the user and performs translations accordingly.

#### Plan

1. Add a selectbox in the sidebar containing the different persona options.
2. Based on selection, retrieve the corresponding system instruction text.
3. If the selection changes, clear the session messages list and re-initialize it with the new `SystemMessage` to start a fresh context.

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

  load_dotenv()

  st.title("💬 Customized Persona Chatbot")

  # Add a selector in the sidebar
  persona = st.sidebar.selectbox(
      "Choose Chatbot Persona",
      ["Helpful Assistant", "Math Tutor", "French Translator"]
  )

  # Map selector to system prompts
  prompts = {
      "Helpful Assistant": "You are a helpful assistant.",
      "Math Tutor": "You are a math tutor. Only answer math questions.",
      "French Translator": "You are a French translator. Translate inputs to French."
  }

  # If persona changes, clear history to apply new system instruction
  if "current_persona" not in st.session_state or st.session_state.current_persona != persona:
      st.session_state.current_persona = persona
      st.session_state.messages = [SystemMessage(content=prompts[persona])]

  if "model" not in st.session_state:
      st.session_state.model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

  # Render message history
  for message in st.session_state.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)

  # Accept user input
  if prompt := st.chat_input("What is on your mind?"):
      with st.chat_message("user"):
          st.markdown(prompt)
      
      st.session_state.messages.append(HumanMessage(content=prompt))
      
      with st.chat_message("assistant"):
          message_placeholder = st.empty()
          with st.spinner("Thinking..."):
              response = st.session_state.model.invoke(st.session_state.messages)
              message_placeholder.markdown(response.content)
              
      st.session_state.messages.append(AIMessage(content=response.content))
  ```
</Accordion>

## Practice & Exercises

To execute the Streamlit chatbot locally, run the script from your terminal:

```bash theme={null}
streamlit run langchain/1_chat_models/6_chat_model_streamlit_bot.py
```
