Spaces:
Running
Running
File size: 7,465 Bytes
9cb6cfa ec6ac20 9cb6cfa ec6ac20 9cb6cfa ec6ac20 9cb6cfa ec6ac20 9cb6cfa ec6ac20 9cb6cfa ec6ac20 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
import streamlit as st
import requests
import os
# Backend URL (change if deployed)
BACKEND_URL = "http://localhost:8000"
st.set_page_config(page_title="PDF Assistant", page_icon="π", layout="wide")
# ---------------- CSS (Dark Theme) ----------------
# FIX: Added CSS for the footer
st.markdown("""
<style>
/* Streamlit standard setup for dark theme adherence */
:root {
--primary-color: #1e3a8a; /* Blue for highlights */
--background-color: #0e1117;
--secondary-background-color: #1a1d29;
--text-color: #f0f2f6;
}
/* Custom Chat Bubbles */
.chat-user {
background: #2d3748; /* Dark gray */
padding: 12px;
border-radius: 10px 10px 2px 10px; /* Rounded corners for chat bubble */
margin: 6px 0 6px auto;
max-width: 85%;
text-align: right;
color: var(--text-color);
}
.chat-bot {
background: var(--primary-color); /* Primary blue */
padding: 12px;
border-radius: 10px 10px 10px 2px;
margin: 6px auto 6px 0;
max-width: 85%;
text-align: left;
color: #ffffff; /* White text for contrast */
}
/* Sources section styling */
.sources {
font-size: 0.8em;
opacity: 0.7;
margin-top: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 5px;
}
/* Footer styling */
.footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: var(--secondary-background-color);
color: var(--text-color);
text-align: center;
padding: 10px;
font-size: 0.85em;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.footer a {
color: var(--primary-color);
text-decoration: none;
font-weight: bold;
}
.footer a:hover {
text-decoration: underline;
}
</style>
""", unsafe_allow_html=True)
# ---------------- SESSION STATE ----------------
if "chat" not in st.session_state:
st.session_state.chat = []
if "uploaded_file_name" not in st.session_state:
st.session_state.uploaded_file_name = None
# Add a key to the file uploader to allow it to be reset.
if "uploader_key" not in st.session_state:
st.session_state.uploader_key = 0
# FIX 1: Change application name
st.title("π PDF Assistant")
# ---------------- FUNCTIONS ----------------
def clear_chat_history():
"""Clears the chat history in the session state."""
st.session_state.chat = []
def clear_memory():
"""Calls the backend endpoint to clear loaded PDF data and resets UI state."""
res = requests.post(f"{BACKEND_URL}/clear")
if res.status_code == 200:
st.session_state.uploaded_file_name = None
# Increment the key of the file uploader to clear its value
st.session_state.uploader_key += 1
st.success("Memory cleared. Please upload a new PDF.")
else:
st.error(f"Failed to clear memory: {res.json().get('detail', 'Unknown error')}")
# Removed st.rerun() to prevent "no-op" warning
# ---------------- SIDEBAR CONTROLS ----------------
with st.sidebar:
st.header("Controls")
st.button("ποΈ Clear Chat History", on_click=clear_chat_history, use_container_width=True)
st.button("π₯ Clear PDF Memory", on_click=clear_memory, use_container_width=True)
st.markdown("---")
if st.session_state.uploaded_file_name:
st.success(f"β
**Active PDF:**\n `{st.session_state.uploaded_file_name}`")
else:
st.warning("β¬οΈ Upload a PDF to start chatting!")
# ---------------- UPLOAD PDF ----------------
# Use the dynamic key for the file uploader.
uploaded = st.file_uploader(
"Upload your PDF",
type=["pdf"],
key=st.session_state.uploader_key # Use the dynamic key
)
# Only process if a file is uploaded AND it's a NEW file
if uploaded and uploaded.name != st.session_state.uploaded_file_name:
st.session_state.uploaded_file_name = None # Clear status while processing
st.session_state.chat = [] # Clear chat for a new document
with st.spinner(f"Processing '{uploaded.name}'..."):
try:
files = {"file": (uploaded.name, uploaded.getvalue(), "application/pdf")}
res = requests.post(f"{BACKEND_URL}/upload", files=files)
if res.status_code == 200:
chunks = res.json().get("chunks", 0)
st.success(f"PDF processed successfully! {chunks} chunks created.")
st.session_state.uploaded_file_name = uploaded.name
else:
error_msg = res.json().get("detail", "Unknown error during processing.")
st.error(f"Upload failed: {error_msg}")
st.session_state.uploaded_file_name = None
except requests.exceptions.ConnectionError:
st.error(f"Could not connect to the backend server at {BACKEND_URL}. Ensure it is running.")
st.session_state.uploaded_file_name = None
except Exception as e:
st.error(f"An unexpected error occurred: {e}")
st.session_state.uploaded_file_name = None
# Rerun the app to update the UI status immediately
st.rerun()
# ---------------- CHAT INPUT ----------------
# Disable input field if no PDF is loaded
disabled_input = st.session_state.uploaded_file_name is None
question = st.text_input(
"Ask a question about the loaded PDF:",
key="question_input",
disabled=disabled_input
)
if st.button("Send", disabled=disabled_input) and question:
# 1. Add user query to chat history
st.session_state.chat.append(("user", question))
# 2. Call backend
with st.spinner("Thinking..."):
try:
res = requests.post(f"{BACKEND_URL}/ask", json={"question": question})
if res.status_code == 200:
data = res.json()
answer = data.get("answer", "No answer provided.")
sources = data.get("sources", 0)
# Format the bot's response to include source count
bot_message = f"{answer}<div class='sources'>Context Chunks Used: {sources}</div>"
st.session_state.chat.append(("bot", bot_message))
else:
error_detail = res.json().get("detail", "Error while generating answer.")
st.session_state.chat.append(("bot", f"π΄ **Error:** {error_detail}"))
except requests.exceptions.ConnectionError:
st.session_state.chat.append(("bot",
f"π΄ **Error:** Could not connect to the backend server at {BACKEND_URL}. Ensure it is running."))
except Exception as e:
st.session_state.chat.append(("bot", f"π΄ **An unexpected error occurred:** {e}"))
# Rerun to display the updated chat history
st.rerun()
# ---------------- SHOW CHAT HISTORY ----------------
st.markdown("## Chat History")
# Reverse the list to show the latest messages at the bottom
for role, msg in st.session_state.chat:
if role == "user":
st.markdown(f"<div class='chat-user'>{msg}</div>", unsafe_allow_html=True)
else:
# Bot message includes the source count, so use the HTML content
st.markdown(f"<div class='chat-bot'>{msg}</div>", unsafe_allow_html=True)
# ---------------- FOOTER (Creator Credit) ----------------
# FIX 2: Add creator credit with LinkedIn link
footer_html = """
<div class="footer">
Created by <a href="https://www.linkedin.com/in/abhishek-iitr/" target="_blank">Abhishek Saxena</a>
</div>
"""
st.markdown(footer_html, unsafe_allow_html=True)
# ---------------- END ---------------- |