Spaces:
Running
Running
import streamlit as st | |
import os | |
import base64 | |
from langchain.memory import ConversationBufferWindowMemory | |
from langchain_community.chat_message_histories import StreamlitChatMessageHistory | |
from utils.ingestion import DocumentProcessor | |
from utils.llm import LLMProcessor | |
from utils.qa import QAEngine | |
# Configure Streamlit page | |
st.set_page_config(page_title="AI-Powered Document QA", layout="wide") | |
# Function to encode image in Base64 for avatars | |
def encode_image(image_path): | |
with open(image_path, "rb") as file: | |
return base64.b64encode(file.read()).decode() | |
# Load avatar images | |
user_avatar = encode_image("./icons/user.jpg") # Change path if needed | |
ai_avatar = encode_image("./icons/ai.jpg") | |
# Initialize document processing & AI components | |
document_processor = DocumentProcessor() | |
llm_processor = LLMProcessor() | |
qa_engine = QAEngine() | |
# Ensure temp directory exists | |
os.makedirs("temp", exist_ok=True) | |
# Sidebar for file upload | |
st.sidebar.header("π Upload a Document") | |
uploaded_file = st.sidebar.file_uploader("Choose a file", type=["pdf", "docx", "html", "pptx", "txt"]) | |
# Initialize chat memory | |
memory_storage = StreamlitChatMessageHistory(key="chat_messages") | |
memory = ConversationBufferWindowMemory( | |
memory_key="chat_history", human_prefix="User", chat_memory=memory_storage, k=5 | |
) | |
# Document upload & processing | |
if uploaded_file and "document_uploaded" not in st.session_state: | |
file_path = os.path.join("temp", uploaded_file.name) | |
with open(file_path, "wb") as f: | |
f.write(uploaded_file.read()) | |
st.sidebar.success("β File uploaded successfully!") | |
with st.spinner("π Processing document..."): | |
document_processor.process_document(file_path) | |
st.sidebar.success("β Document processed successfully!") | |
st.session_state["document_uploaded"] = True | |
# Chat UI Header | |
st.title("π¬ AI Chat Assistant") | |
st.divider() | |
# Display chat history | |
for idx, message in enumerate(memory_storage.messages): | |
role = "user" if message.type == "human" else "assistant" | |
avatar = user_avatar if role == "user" else ai_avatar # Assign appropriate avatar | |
with st.chat_message(role, avatar=f"data:image/jpeg;base64,{avatar}"): | |
st.markdown(message.content) | |
# Add copy button only for AI messages | |
if role == "assistant": | |
copy_key = f"copy_btn_{idx}" # Unique key for each copy button | |
st.code(message.content, language="text") # Display code block with text formatting | |
st.button("π Copy", key=copy_key, on_click=lambda text=message.content: st.session_state.update({"copied_text": text})) | |
# User input at the bottom | |
user_input = st.chat_input("Type your message here...") | |
if user_input: | |
memory_storage.add_user_message(user_input) | |
# Display user message | |
with st.chat_message("user", avatar=f"data:image/jpeg;base64,{user_avatar}"): | |
st.markdown(user_input) | |
with st.spinner("π€ Thinking..."): | |
if st.session_state.get("document_uploaded", False): | |
answer = qa_engine.query(user_input) | |
else: | |
answer = llm_processor.generate_answer("", user_input) | |
st.warning("β οΈ No document uploaded. Response is from general AI knowledge.") | |
memory_storage.add_ai_message(answer) | |
# Display AI response with copy button | |
with st.chat_message("assistant", avatar=f"data:image/jpeg;base64,{ai_avatar}"): | |
st.markdown(answer.content) | |
# Copy button for AI response | |
st.code(answer.content, language="text") | |
if st.button("π Copy", key="copy_latest"): | |
st.session_state["copied_text"] = answer.content | |
st.success("β Copied to clipboard!") | |