Building a simple chat application using Streamlit and Langchain
Sometimes you want to set up a user interface for your chat application. This post discusses setting up such an interface using Streamlit - an open-source app framework that "turns data into shareable web apps in minutes".
Setting Up Your Environment
The full code for this post is on Github.
Install dependencies
You will need to install streamlit, langchain, and other required dependencies. Follow the instructions from the repo.
Project structure
For the simple chat script, the project consists of three main components:
- runner.py: Contains the SimpleRunner class, responsible for managing chat sessions and processing messages.
- utils.py: Includes utility functions like show_chat_history(), which displays previous chat interactions.
- The main script (multi_turn_chat.py): Ties everything together, creating the web interface and handling user input.
Main script
The main script sets up the UI, and is very simple. Essentially, it looks like:
def main():
runner = SimpleRunner(session_key='chat-with-assistant')
st.title("Let's chat - Please ask me anything")
show_chat_history(runner.get_history())
if question := st.chat_input():
with st.expander(question, True):
st.chat_message("human").write(question)
with st.spinner("Thinking..."):
response = runner.run(question)
st.chat_message("ai").write(response.content)
- Initializing the Runner: We create a
SimpleRunner
instance, responsible for managing the chat session. - Setting Up Streamlit Interface:
st.title()
sets the title of our web page, andshow_chat_history()
displays past interactions. - Handling Chat Input:
st.chat_input()
captures the user's question. If a question is asked, we display it and the AI's response in an expander with a loading spinner.
Running the Application
To run your Streamlit application, execute the following command in your terminal:
streamlit run multi_turn_chat.py
This command launches a local web server, and you should see your chat application running in your default web browser.
Extending the Chat Application with Semantic Search
The basic chat application we've discussed above serves as a great starting point. However, in many scenarios, you might want your chat application to understand and leverage existing documents or knowledge bases to provide more accurate and helpful responses. The script 'chat_with_documents.py' enhances the simple application with semantic search capabilities using an in-memory document index.
Enhancements Overview
- Document Uploading: Allow users to upload documents (PDFs and text files) that the chat application can reference.
- Document Indexing: Implement an in-memory document index to store and search uploaded document content.
- Semantic Search: Use
RunnerWithSemanticSearch
instead ofSimpleRunner
for querying the document index to provide context-aware responses.
Handle Document Uploads and Indexing
First, add the ability for users to upload documents directly through the Streamlit interface. The uploaded documents are then embedded into the FAISS vector store via the InMemoryDocumentIndex
:
def file_uploads(document_index):
uploaded_files = st.file_uploader("Upload documents", type=["pdf", "txt"], accept_multiple_files=True)
if uploaded_files:
for uploaded_file in uploaded_files:
# Process and add each uploaded file to the document index
document_index.add_to_index(uploaded_file)
Initializing the Document Index
We initialise the InMemoryDocumentIndex
object which is a wrapper around FAISS once, to avoid writing over the stored vectors:
def initialize_document_index():
if 'document_index' not in st.session_state:
data_loader = DataLoader()
st.session_state.document_index = InMemoryDocumentIndex(data_loader)
return st.session_state.document_index
Enhanced main function
The main function now uses RunnerWithSemanticSearch
, which leverages the document index for generating responses:
def main():
runner = RunnerWithSemanticSearch(session_key='chat-with-with-documents')
# Set up the Streamlit web interface
st.title("Query an AI using semantic search")
# Display all messages in the history
show_chat_history(runner.get_history())
document_index = initialize_document_index()
# Handle input from the chat input box
if question := st.chat_input():
with st.expander(question, True):
st.chat_message("human").write(question)
with st.spinner("Thinking..."):
response = runner.run(question, document_index)
st.chat_message("ai").write(response.content)
Putting It All Together
Finally, integrate the document upload functionality with the main chat interface, allowing users to choose whether to upload documents:
if __name__ == "__main__":
upload_files = st.checkbox("Upload files?", False)
if upload_files:
document_index = initialize_document_index()
file_uploads(document_index)
else:
main()
Run using:
streamlit run chat_with_your_documents.py
And that's it
By extending our chat application with semantic search capabilities, we've significantly increased its usefulness and applicability. Whether it's for a customer support system that can pull information from manuals and FAQs, or for an educational chatbot that references course materials, the ability to understand and leverage a vast array of documents opens up new possibilities for interaction and assistance.
Docs
For further exploration, please refer to the following documentation: