In Streamlit, the st.chat_box and st.message are able to create a chatbot. Here is a working example (created iteratively with ChatGPT)
import streamlit as st
import numpy as np
from openai import AzureOpenAI
from dotenv import load_dotenv
import os
from tinydb import TinyDB
from datetime import datetime
import pytz # For timezone handling
import uuid
import pandas as pd
from gtts import gTTS
from io import BytesIO
from langdetect import detect
import time, whisper, librosa
# Load variables from .env
load_dotenv(override=True)
# define the APP ID:
appid='sel'
# Set the page configuration to wide mode
st.set_page_config(layout="wide")
# define whisper model
@st.cache_resource
def load_whisper():
model=whisper.load_model('small.en')
return model
# define the function to load client
@st.cache_resource
def get_azure_openai_client():
client = AzureOpenAI(api_key = os.getenv("AZURE_OPENAI_API_KEY"),
api_version= os.getenv("OPENAI_API_VERSION"),
azure_endpoint= os.getenv("AZURE_OPENAI_ENDPOINT"))
return client
# paly audio generate by gTTS
def play_audio(text,autoplay=False):
try:
text_language=detect(text)
except:
text_language='en'
tts = gTTS(text,lang=text_language)
mp3_fp = BytesIO()
tts.write_to_fp(mp3_fp)
mp3_fp.seek(0)
st.audio(mp3_fp,format="audio/mp3",autoplay=autoplay)
def input_audio_to_text():
audio_data = st.audio_input(":red[Click the microphone button to talk, and then click the stop button to stop and submit]")
if audio_data:
#with open('my_audio.wav',"wb") as f:
# f.write(audio_data.read())
audio_array,_ = librosa.load(audio_data,sr=16000)
text_msg = whisper_model.transcribe(audio_array)['text']
return text_msg
@st.fragment
def chat_box_fragment():
container = st.container(height=550)
for message in st.session_state.messages:
if message['event_name'] == 'chat': # only display chat
with container.chat_message(message["role"]):
st.markdown(message["content"])
# Accept user new input
if show_audio:
prompt = input_audio_to_text()
else:
prompt = st.chat_input('Enter to chat')
if prompt:
#if prompt := st.chat_input("Enter to chat"):
# Add user message to chat history
st.session_state.messages.append({"role": "user", "content": prompt,"datetime":get_eastern_time(),"user_id":userid,"session_id":st.session_state.session_id,"app_id":appid,"event_name":"chat"})
# add the user message to database
db_table.insert(st.session_state.messages[-1])
# display the user input chat
with container.chat_message("user"):
st.markdown(prompt)
# get the response from llm
with container.chat_message("assistant"):
try:
stream = client.chat.completions.create(
model="gpt-4o",
messages=[{"role":"system","content":st.session_state.system_instruction}]+
[{"role": m["role"], "content": m["content"]}
for m in st.session_state.messages],
stream=True
)
response=st.write_stream(stream)
except:
response='Please avoid saying things that are not appropriate in classroom!'
st.write(response)
# write the response into chat history
st.session_state.messages.append({"role": "assistant", "content": response,"datetime":get_eastern_time(),"user_id":userid,"session_id":st.session_state.session_id,"app_id":appid,"event_name":"chat"})
# add the response into database
db_table.insert(st.session_state.messages[-1])
# play audio if checked
if show_audio:
with container:
play_audio(response,autoplay=True)
#--------define the question fragment---------------
@st.fragment
def display_questions_sel():
st.markdown("**Question**: In less than 300 words, introduce how transformer works.")
answer = st.text_area(
label="",
key="item_1",
height=150
)
# load the whisper model
whisper_model = load_whisper()
# Define the Eastern Time Zone
eastern_tz = pytz.timezone("US/Eastern")
# Function to get current Eastern Time
def get_eastern_time():
return datetime.now(eastern_tz).strftime("%Y-%m-%d %H:%M:%S")
# Initialize TinyDB
#db = TinyDB("log/chat_messages_sel.json") # JSON file to store chat messages
db = TinyDB("user_logs/user_log.json")
db_table = db.table('sel_listening')
#### -------- webpage ----------------####
html_string = '''<p align="center"> <font size="10.5"> Your App Name </font></p>'''
st.html(html_string)
st.markdown('***')
st.markdown('## About')
st.markdown('This is an example of creating a streamlit chatbot using LLM. For questions and support, please contact: <jianganghao@gmali.com')
# --- login -----
st.markdown('## Login')
col_login = st.columns([2,1,2,2])
userid=col_login[0].text_input('Please enter your username (nickname):')
access_token=col_login[2].text_input('Please enter your access token:', type="password")
if not access_token == os.getenv('PASS_TOKEN'):
st.markdown('#### :blue[Sorry, you either did not enter the login credential or the token is invalid. Please try again or contact the administrator.]')
st.stop()
# ------ refresh the session_state upon first loading -----
if "app_page" not in st.session_state:
st.session_state.app_page = appid
if "user_id" not in st.session_state:
st.session_state.user_id = userid
if st.session_state.app_page != appid or st.session_state.user_id != userid:
st.session_state.clear()
st.session_state.app_page=appid # after clear, set the current appdid
# ---- system instruction
if "system_instruction" not in st.session_state:
st.session_state.system_instruction = '''You are an experienced teacher on *******. Today, you are going to help students with *****
So, in your conversation with the user, please ask the user some questions to make sure he or she understand the topic.
Please make sure you use appropriate language suitable for the age and make each turn of the chat not very long.
'''
if userid=='admin':
with st.expander("As admin, you can modifty the instruction to AI agent here:"):
st.session_state.system_instruction = st.text_area('',value=st.session_state.system_instruction,height=400)
# Create a downloadable file with the text string
st.download_button(
label="Click to download the prompt instruction", # Button label
data=st.session_state.system_instruction.encode('utf-8'), # Data to download
file_name="my_prompt.txt", # Default file name
mime="text/plain" # MIME type for text files
)
st.markdown('***')
# ----------------------------------------------------------
# Initiate ChatGPT client
client = get_azure_openai_client()
# Generate a short unique session ID
if "session_id" not in st.session_state:
st.session_state.session_id = str(uuid.uuid4())[:8]
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
st.session_state.messages.append({"role": "assistant", "content": "Hi, I am your AI learning assistant. I am here to help you practice the skill you learned today! Let's start!","datetime":get_eastern_time(),"user_id":userid,"session_id":st.session_state.session_id,"app_id":appid,"event_name":"chat"})
db_table.insert(st.session_state.messages[-1])
st.session_state.messages.append({"role": "assistant", "content": "Please navigate through the slides on the left first and then chat with me about the skills","datetime":get_eastern_time(),"user_id":userid,"session_id":st.session_state.session_id,"app_id":appid,"event_name":"chat"})
db_table.insert(st.session_state.messages[-1])
#------------
st.markdown('## Step 1: Go Through the Learning Materials with the Help of AI')
# Left and right region
col1, col2 = st.columns([2,1])
# Left column (Iframe)
with col1:
# clear the chat history by the same user
if st.button('Click to clear history and refresh'):
st.session_state.messages = st.session_state.messages[0:2]
# Replace the URL with the webpage you want to embed
iframe_code = """
<iframe src="https://www.youtube.com/watch?v=wjZofJX0v4M"
width="100%"
height="600"
style="border: none;"></iframe>
"""
st.components.v1.html(iframe_code, height=600)
with col2:
#st.markdown('<h4 style="text-align: center;"><strong>AI Learning Assistant</strong></h4>', unsafe_allow_html=True)
show_audio = st.checkbox('Enable Audio Chat')
chat_box_fragment_sel()
st.markdown("***")
st.markdown('## Step 2: Answer the Following Question')
with st.container(height=400):
display_questions_sel()
if userid == "admin":
st.markdown('## Analytics')
with st.expander('Click to see the details'):
df = pd.DataFrame(st.session_state.messages)
df['datetime'] = pd.to_datetime(df['datetime'])
df['session_time'] = df.datetime - df.datetime.iloc[0]
df['session_time'] = df.session_time.apply(lambda x: x.total_seconds())
st.markdown(f"""#### Total number of turns: {df.query('role=="user"').content.count()*2+1}""")
st.markdown(f"""#### Total number of words by user: {df.query('role=="user"').content.apply(lambda x: len(x.split())).sum()}""")
st.markdown(f"""#### Total number of words by Agent: {df.query('role=="assistant"').content.apply(lambda x: len(x.split())).sum()}""")
st.dataframe(df)