Update app.py
Browse files
app.py
CHANGED
|
@@ -1,69 +1,647 @@
|
|
| 1 |
-
import
|
| 2 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
hf_token: gr.OAuthToken,
|
| 13 |
-
):
|
| 14 |
-
"""
|
| 15 |
-
For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
|
| 16 |
-
"""
|
| 17 |
-
client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
|
| 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 |
-
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PyPDF2 import PdfReader
|
| 3 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 4 |
+
from langchain_huggingface import HuggingFaceEmbeddings, HuggingFacePipeline
|
| 5 |
+
from langchain_community.vectorstores import FAISS
|
| 6 |
+
from langchain.prompts import PromptTemplate
|
| 7 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
| 8 |
+
import torch
|
| 9 |
+
import time
|
| 10 |
+
import base64
|
| 11 |
|
| 12 |
+
st.set_page_config(
|
| 13 |
+
page_title="QueryDocs AI",
|
| 14 |
+
page_icon="π",
|
| 15 |
+
layout="wide",
|
| 16 |
+
initial_sidebar_state="expanded"
|
| 17 |
+
)
|
| 18 |
|
| 19 |
+
# βββ HELPERS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 20 |
+
def img_to_base64(path):
|
| 21 |
+
try:
|
| 22 |
+
with open(path, "rb") as f:
|
| 23 |
+
return base64.b64encode(f.read()).decode()
|
| 24 |
+
except:
|
| 25 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
# βββ GLOBAL CSS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 28 |
+
st.markdown("""
|
| 29 |
+
<style>
|
| 30 |
+
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@400;600;700&family=Orbitron:wght@700;900&display=swap');
|
| 31 |
|
| 32 |
+
/* ββ Base ββ */
|
| 33 |
+
html, body, [data-testid="stAppViewContainer"] {
|
| 34 |
+
background: #0d1117 !important;
|
| 35 |
+
color: #c9d1d9 !important;
|
| 36 |
+
font-family: 'Rajdhani', sans-serif !important;
|
| 37 |
+
}
|
| 38 |
+
[data-testid="stSidebar"] {
|
| 39 |
+
background: #161b22 !important;
|
| 40 |
+
border-right: 1px solid #30363d !important;
|
| 41 |
+
}
|
| 42 |
+
[data-testid="stSidebar"] * { color: #c9d1d9 !important; }
|
| 43 |
+
footer, #MainMenu { visibility: hidden; }
|
| 44 |
|
| 45 |
+
/* ββ Header ββ */
|
| 46 |
+
.app-header {
|
| 47 |
+
display: flex; align-items: center; gap: 14px;
|
| 48 |
+
padding: 16px 0 14px;
|
| 49 |
+
border-bottom: 1px solid #21262d;
|
| 50 |
+
margin-bottom: 20px;
|
| 51 |
+
animation: fadeInDown 0.5s ease both;
|
| 52 |
+
}
|
| 53 |
+
.app-title {
|
| 54 |
+
font-family: 'Orbitron', monospace;
|
| 55 |
+
font-size: 1.6rem; font-weight: 900;
|
| 56 |
+
color: #fff; letter-spacing: 2px;
|
| 57 |
+
}
|
| 58 |
+
.app-title span { color: #58a6ff; }
|
| 59 |
+
.app-sub {
|
| 60 |
+
font-family: 'Share Tech Mono', monospace;
|
| 61 |
+
font-size: 0.68rem; color: #8b949e;
|
| 62 |
+
letter-spacing: 3px; margin-top: 3px;
|
| 63 |
+
}
|
| 64 |
|
| 65 |
+
/* ββ Profile card in sidebar ββ */
|
| 66 |
+
.profile-card {
|
| 67 |
+
text-align: center;
|
| 68 |
+
padding: 20px 0 16px;
|
| 69 |
+
border-bottom: 1px solid #21262d;
|
| 70 |
+
margin-bottom: 16px;
|
| 71 |
+
animation: fadeInDown 0.5s ease both;
|
| 72 |
+
}
|
| 73 |
+
.profile-avatar {
|
| 74 |
+
width: 72px; height: 72px;
|
| 75 |
+
border-radius: 50%;
|
| 76 |
+
overflow: hidden;
|
| 77 |
+
border: 2px solid #58a6ff;
|
| 78 |
+
box-shadow: 0 0 0 3px rgba(88,166,255,0.15),
|
| 79 |
+
0 0 20px rgba(88,166,255,0.2);
|
| 80 |
+
margin: 0 auto 10px;
|
| 81 |
+
animation: pulse-av 2.5s ease-in-out infinite;
|
| 82 |
+
}
|
| 83 |
+
.profile-avatar img {
|
| 84 |
+
width: 100%; height: 100%;
|
| 85 |
+
object-fit: cover; border-radius: 50%;
|
| 86 |
+
}
|
| 87 |
+
@keyframes pulse-av {
|
| 88 |
+
0%,100%{box-shadow:0 0 0 3px rgba(88,166,255,0.15),0 0 20px rgba(88,166,255,0.2);}
|
| 89 |
+
50% {box-shadow:0 0 0 4px rgba(88,166,255,0.3),0 0 30px rgba(88,166,255,0.35);}
|
| 90 |
+
}
|
| 91 |
+
.profile-name {
|
| 92 |
+
font-family: 'Orbitron', monospace;
|
| 93 |
+
font-size: 0.82rem; font-weight: 700;
|
| 94 |
+
color: #fff; letter-spacing: 1px;
|
| 95 |
+
}
|
| 96 |
+
.profile-role {
|
| 97 |
+
font-family: 'Share Tech Mono', monospace;
|
| 98 |
+
font-size: 0.64rem; color: #58a6ff;
|
| 99 |
+
letter-spacing: 2px; margin-top: 3px;
|
| 100 |
+
}
|
| 101 |
+
.profile-links {
|
| 102 |
+
display: flex; justify-content: center;
|
| 103 |
+
gap: 8px; margin-top: 10px; flex-wrap: wrap;
|
| 104 |
+
}
|
| 105 |
+
.p-link {
|
| 106 |
+
font-family: 'Share Tech Mono', monospace;
|
| 107 |
+
font-size: 0.62rem; color: #8b949e !important;
|
| 108 |
+
text-decoration: none !important;
|
| 109 |
+
background: #0d1117;
|
| 110 |
+
border: 1px solid #30363d;
|
| 111 |
+
padding: 3px 10px; border-radius: 20px;
|
| 112 |
+
transition: all 0.25s;
|
| 113 |
+
}
|
| 114 |
+
.p-link:hover { color: #58a6ff !important; border-color: #58a6ff; }
|
| 115 |
|
| 116 |
+
/* ββ Upload zone ββ */
|
| 117 |
+
.upload-card {
|
| 118 |
+
background: #161b22;
|
| 119 |
+
border: 2px dashed #30363d;
|
| 120 |
+
border-radius: 12px;
|
| 121 |
+
padding: 32px;
|
| 122 |
+
text-align: center;
|
| 123 |
+
transition: all 0.3s ease;
|
| 124 |
+
animation: fadeInUp 0.5s ease both;
|
| 125 |
+
}
|
| 126 |
+
.upload-card:hover { border-color: #58a6ff; }
|
| 127 |
+
.upload-icon { font-size: 2.5rem; margin-bottom: 10px; }
|
| 128 |
+
.upload-title {
|
| 129 |
+
font-family: 'Orbitron', monospace;
|
| 130 |
+
font-size: 0.95rem; color: #fff; margin-bottom: 6px;
|
| 131 |
+
}
|
| 132 |
+
.upload-sub { font-size: 0.85rem; color: #8b949e; }
|
| 133 |
|
| 134 |
+
/* ββ PDF info banner ββ */
|
| 135 |
+
.pdf-banner {
|
| 136 |
+
background: #161b22;
|
| 137 |
+
border: 1px solid #21262d;
|
| 138 |
+
border-left: 3px solid #58a6ff;
|
| 139 |
+
border-radius: 8px;
|
| 140 |
+
padding: 12px 18px;
|
| 141 |
+
display: flex; align-items: center; gap: 12px;
|
| 142 |
+
margin-bottom: 16px;
|
| 143 |
+
animation: fadeInUp 0.4s ease both;
|
| 144 |
+
}
|
| 145 |
+
.pdf-name {
|
| 146 |
+
font-weight: 700; font-size: 0.95rem; color: #fff;
|
| 147 |
+
}
|
| 148 |
+
.pdf-meta {
|
| 149 |
+
font-family: 'Share Tech Mono', monospace;
|
| 150 |
+
font-size: 0.68rem; color: #8b949e; margin-top: 2px;
|
| 151 |
+
}
|
| 152 |
|
| 153 |
+
/* ββ Chat bubbles ββ */
|
| 154 |
+
.chat-wrap { display: flex; flex-direction: column; gap: 16px; padding: 8px 0; }
|
| 155 |
+
.msg-user { display: flex; justify-content: flex-end; animation: slideInR 0.3s ease both; }
|
| 156 |
+
.msg-ai { display: flex; justify-content: flex-start; animation: slideInL 0.3s ease both; }
|
| 157 |
+
.bubble-user {
|
| 158 |
+
background: linear-gradient(135deg, #1f6feb, #388bfd);
|
| 159 |
+
color: #fff; padding: 12px 18px;
|
| 160 |
+
border-radius: 18px 18px 4px 18px;
|
| 161 |
+
max-width: 75%; font-size: 0.97rem; line-height: 1.6;
|
| 162 |
+
box-shadow: 0 4px 15px rgba(31,111,235,0.25);
|
| 163 |
+
}
|
| 164 |
+
.bubble-ai {
|
| 165 |
+
background: #161b22; color: #c9d1d9;
|
| 166 |
+
padding: 14px 18px;
|
| 167 |
+
border-radius: 18px 18px 18px 4px;
|
| 168 |
+
max-width: 80%; font-size: 0.95rem; line-height: 1.75;
|
| 169 |
+
border: 1px solid #30363d;
|
| 170 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
|
| 171 |
+
}
|
| 172 |
+
.bubble-ai .answer-label {
|
| 173 |
+
font-family: 'Share Tech Mono', monospace;
|
| 174 |
+
font-size: 0.65rem; color: #58a6ff;
|
| 175 |
+
letter-spacing: 2px; margin-bottom: 8px;
|
| 176 |
+
text-transform: uppercase;
|
| 177 |
+
}
|
| 178 |
+
.msg-meta {
|
| 179 |
+
font-family: 'Share Tech Mono', monospace;
|
| 180 |
+
font-size: 0.62rem; color: #484f58;
|
| 181 |
+
margin-top: 4px; padding: 0 6px;
|
| 182 |
+
}
|
| 183 |
|
| 184 |
+
/* ββ Source chunks ββ */
|
| 185 |
+
.sources-wrap {
|
| 186 |
+
margin-top: 10px;
|
| 187 |
+
border-top: 1px solid #21262d;
|
| 188 |
+
padding-top: 10px;
|
| 189 |
+
}
|
| 190 |
+
.source-label {
|
| 191 |
+
font-family: 'Share Tech Mono', monospace;
|
| 192 |
+
font-size: 0.62rem; color: #8b949e;
|
| 193 |
+
letter-spacing: 2px; margin-bottom: 6px;
|
| 194 |
+
}
|
| 195 |
+
.source-chip {
|
| 196 |
+
display: inline-block;
|
| 197 |
+
background: rgba(88,166,255,0.08);
|
| 198 |
+
border: 1px solid rgba(88,166,255,0.2);
|
| 199 |
+
color: #58a6ff; padding: 3px 10px;
|
| 200 |
+
border-radius: 4px; font-size: 0.72rem;
|
| 201 |
+
font-family: 'Share Tech Mono', monospace;
|
| 202 |
+
margin: 3px 3px; cursor: pointer;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/* ββ Typing indicator ββ */
|
| 206 |
+
.typing-wrap { display: flex; justify-content: flex-start; }
|
| 207 |
+
.typing-box {
|
| 208 |
+
display: flex; align-items: center; gap: 5px;
|
| 209 |
+
padding: 14px 18px; background: #161b22;
|
| 210 |
+
border: 1px solid #30363d;
|
| 211 |
+
border-radius: 18px 18px 18px 4px;
|
| 212 |
+
}
|
| 213 |
+
.t-dot {
|
| 214 |
+
width: 7px; height: 7px; background: #58a6ff;
|
| 215 |
+
border-radius: 50%;
|
| 216 |
+
animation: tdot 1.2s ease-in-out infinite;
|
| 217 |
+
}
|
| 218 |
+
.t-dot:nth-child(2){animation-delay:0.2s;}
|
| 219 |
+
.t-dot:nth-child(3){animation-delay:0.4s;}
|
| 220 |
+
@keyframes tdot{0%,60%,100%{transform:translateY(0);opacity:0.4;}30%{transform:translateY(-8px);opacity:1;}}
|
| 221 |
+
|
| 222 |
+
/* ββ Input ββ */
|
| 223 |
+
.stTextInput > div > div > input {
|
| 224 |
+
background: #161b22 !important;
|
| 225 |
+
border: 1px solid #30363d !important;
|
| 226 |
+
border-radius: 8px !important;
|
| 227 |
+
color: #c9d1d9 !important;
|
| 228 |
+
font-family: 'Rajdhani', sans-serif !important;
|
| 229 |
+
font-size: 0.97rem !important;
|
| 230 |
+
}
|
| 231 |
+
.stTextInput > div > div > input:focus {
|
| 232 |
+
border-color: #58a6ff !important;
|
| 233 |
+
box-shadow: 0 0 12px rgba(88,166,255,0.15) !important;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
/* ββ Buttons ββ */
|
| 237 |
+
.stButton > button {
|
| 238 |
+
background: #21262d !important; color: #c9d1d9 !important;
|
| 239 |
+
border: 1px solid #30363d !important; border-radius: 8px !important;
|
| 240 |
+
font-family: 'Share Tech Mono', monospace !important;
|
| 241 |
+
font-size: 0.73rem !important; letter-spacing: 1px !important;
|
| 242 |
+
transition: all 0.25s !important;
|
| 243 |
+
}
|
| 244 |
+
.stButton > button:hover {
|
| 245 |
+
background: #30363d !important;
|
| 246 |
+
border-color: #58a6ff !important; color: #58a6ff !important;
|
| 247 |
+
}
|
| 248 |
+
.send-btn > button {
|
| 249 |
+
background: linear-gradient(135deg,#1f6feb,#388bfd) !important;
|
| 250 |
+
color: #fff !important; border: none !important;
|
| 251 |
+
box-shadow: 0 0 16px rgba(31,111,235,0.3) !important;
|
| 252 |
+
}
|
| 253 |
+
.send-btn > button:hover {
|
| 254 |
+
box-shadow: 0 0 28px rgba(31,111,235,0.55) !important;
|
| 255 |
+
transform: translateY(-2px) !important; color: #fff !important;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
/* ββ Stats ββ */
|
| 259 |
+
.stat-row {
|
| 260 |
+
display: flex; gap: 10px; flex-wrap: wrap;
|
| 261 |
+
margin-bottom: 14px;
|
| 262 |
+
}
|
| 263 |
+
.stat-box {
|
| 264 |
+
flex: 1; min-width: 80px;
|
| 265 |
+
background: #161b22; border: 1px solid #21262d;
|
| 266 |
+
border-top: 2px solid #58a6ff; border-radius: 6px;
|
| 267 |
+
padding: 10px 12px; text-align: center;
|
| 268 |
+
}
|
| 269 |
+
.stat-num {
|
| 270 |
+
font-family: 'Orbitron', monospace;
|
| 271 |
+
font-size: 1.2rem; font-weight: 900; color: #58a6ff;
|
| 272 |
+
}
|
| 273 |
+
.stat-lbl {
|
| 274 |
+
font-family: 'Share Tech Mono', monospace;
|
| 275 |
+
font-size: 0.6rem; color: #8b949e;
|
| 276 |
+
letter-spacing: 1px; margin-top: 3px;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
/* ββ Welcome ββ */
|
| 280 |
+
.welcome-card {
|
| 281 |
+
background: #161b22; border: 1px solid #21262d;
|
| 282 |
+
border-radius: 12px; padding: 30px;
|
| 283 |
+
text-align: center; margin: 10px 0;
|
| 284 |
+
animation: fadeInUp 0.6s ease both;
|
| 285 |
+
}
|
| 286 |
+
.wc-icon { font-size: 2.8rem; margin-bottom: 12px; }
|
| 287 |
+
.wc-title {
|
| 288 |
+
font-family: 'Orbitron', monospace;
|
| 289 |
+
font-size: 1rem; color: #fff; margin-bottom: 8px;
|
| 290 |
+
}
|
| 291 |
+
.wc-sub { font-size: 0.9rem; color: #8b949e; line-height: 1.65; }
|
| 292 |
+
.tip-chip {
|
| 293 |
+
display: inline-block;
|
| 294 |
+
background: rgba(88,166,255,0.08);
|
| 295 |
+
border: 1px solid rgba(88,166,255,0.25);
|
| 296 |
+
color: #58a6ff; padding: 4px 12px;
|
| 297 |
+
border-radius: 20px; margin: 4px 3px;
|
| 298 |
+
font-family: 'Share Tech Mono', monospace;
|
| 299 |
+
font-size: 0.67rem;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/* ββ File uploader ββ */
|
| 303 |
+
[data-testid="stFileUploader"] {
|
| 304 |
+
background: #161b22 !important;
|
| 305 |
+
border: 2px dashed #30363d !important;
|
| 306 |
+
border-radius: 10px !important;
|
| 307 |
+
}
|
| 308 |
+
[data-testid="stFileUploader"]:hover {
|
| 309 |
+
border-color: #58a6ff !important;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
/* ββ Glow divider ββ */
|
| 313 |
+
.glow-div {
|
| 314 |
+
border: none; height: 1px;
|
| 315 |
+
background: linear-gradient(90deg,transparent,#58a6ff,transparent);
|
| 316 |
+
margin: 16px 0; box-shadow: 0 0 8px rgba(88,166,255,0.3);
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
/* ββ Animations ββ */
|
| 320 |
+
@keyframes fadeInDown { from{opacity:0;transform:translateY(-16px)} to{opacity:1;transform:translateY(0)} }
|
| 321 |
+
@keyframes fadeInUp { from{opacity:0;transform:translateY(16px)} to{opacity:1;transform:translateY(0)} }
|
| 322 |
+
@keyframes slideInR { from{opacity:0;transform:translateX(20px)} to{opacity:1;transform:translateX(0)} }
|
| 323 |
+
@keyframes slideInL { from{opacity:0;transform:translateX(-20px)} to{opacity:1;transform:translateX(0)} }
|
| 324 |
+
|
| 325 |
+
::-webkit-scrollbar { width: 5px; }
|
| 326 |
+
::-webkit-scrollbar-track { background: #0d1117; }
|
| 327 |
+
::-webkit-scrollbar-thumb { background: #30363d; border-radius: 3px; }
|
| 328 |
+
::-webkit-scrollbar-thumb:hover { background: #58a6ff; }
|
| 329 |
+
</style>
|
| 330 |
+
""", unsafe_allow_html=True)
|
| 331 |
+
|
| 332 |
+
# βββ SESSION STATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 333 |
+
if "messages" not in st.session_state: st.session_state.messages = []
|
| 334 |
+
if "vectorstore" not in st.session_state: st.session_state.vectorstore = None
|
| 335 |
+
if "pdf_name" not in st.session_state: st.session_state.pdf_name = None
|
| 336 |
+
if "pdf_pages" not in st.session_state: st.session_state.pdf_pages = 0
|
| 337 |
+
if "pdf_chunks" not in st.session_state: st.session_state.pdf_chunks = 0
|
| 338 |
+
if "q_count" not in st.session_state: st.session_state.q_count = 0
|
| 339 |
+
|
| 340 |
+
# βββ MODEL LOADERS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 341 |
+
@st.cache_resource(show_spinner=False)
|
| 342 |
+
def load_embeddings():
|
| 343 |
+
return HuggingFaceEmbeddings(
|
| 344 |
+
model_name="sentence-transformers/all-MiniLM-L6-v2"
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
@st.cache_resource(show_spinner=False)
|
| 348 |
+
def load_llm():
|
| 349 |
+
model_id = "TinyLlama/TinyLlama-1.1B-chat-v1.0"
|
| 350 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 351 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 352 |
+
model_id, device_map="auto",
|
| 353 |
+
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
|
| 354 |
+
)
|
| 355 |
+
pipe = pipeline(
|
| 356 |
+
"text-generation", model=model, tokenizer=tokenizer,
|
| 357 |
+
max_new_tokens=512, temperature=0.3, do_sample=True,
|
| 358 |
+
pad_token_id=tokenizer.eos_token_id, repetition_penalty=1.1
|
| 359 |
+
)
|
| 360 |
+
return HuggingFacePipeline(pipeline=pipe)
|
| 361 |
+
|
| 362 |
+
# βββ PDF PROCESSOR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 363 |
+
def process_pdf(uploaded_file):
|
| 364 |
+
reader = PdfReader(uploaded_file)
|
| 365 |
+
raw_text = ""
|
| 366 |
+
for page in reader.pages:
|
| 367 |
+
text = page.extract_text()
|
| 368 |
+
if text:
|
| 369 |
+
raw_text += text
|
| 370 |
+
|
| 371 |
+
if not raw_text.strip():
|
| 372 |
+
raise ValueError("No readable text found. PDF may be scanned/image-based.")
|
| 373 |
+
|
| 374 |
+
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
| 375 |
+
chunks = splitter.split_text(raw_text)
|
| 376 |
+
embeddings = load_embeddings()
|
| 377 |
+
vectorstore = FAISS.from_texts(chunks, embeddings)
|
| 378 |
+
return vectorstore, len(reader.pages), len(chunks)
|
| 379 |
+
|
| 380 |
+
# βββ ANSWER FUNCTION ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 381 |
+
def get_answer(question, vectorstore):
|
| 382 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
|
| 383 |
+
relevant_docs = retriever.invoke(question)
|
| 384 |
+
context = "\n\n".join([f"---\n{doc.page_content}" for doc in relevant_docs])
|
| 385 |
+
sources = [doc.page_content[:120] + "..." for doc in relevant_docs]
|
| 386 |
+
|
| 387 |
+
prompt_template = PromptTemplate(
|
| 388 |
+
input_variables=["context", "question"],
|
| 389 |
+
template="""<|system|>
|
| 390 |
+
You are QueryDocs AI, an intelligent document assistant. Use ONLY the context provided to answer the question clearly and accurately. If the answer is not in the context, say so honestly.
|
| 391 |
+
<|user|>
|
| 392 |
+
CONTEXT:
|
| 393 |
+
{context}
|
| 394 |
+
|
| 395 |
+
QUESTION:
|
| 396 |
+
{question}
|
| 397 |
+
<|assistant|>
|
| 398 |
"""
|
| 399 |
+
)
|
| 400 |
+
llm = load_llm()
|
| 401 |
+
chain = prompt_template | llm
|
| 402 |
+
result = chain.invoke({"context": context, "question": question})
|
| 403 |
+
|
| 404 |
+
# extract only the assistant reply
|
| 405 |
+
if "<|assistant|>" in result:
|
| 406 |
+
answer = result.split("<|assistant|>")[-1].strip()
|
| 407 |
+
else:
|
| 408 |
+
answer = result.strip()
|
| 409 |
+
|
| 410 |
+
return answer, sources
|
| 411 |
+
|
| 412 |
+
# βββ SIDEBAR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 413 |
+
with st.sidebar:
|
| 414 |
+
# Profile card
|
| 415 |
+
img_b64 = img_to_base64("assets/NANII.png")
|
| 416 |
+
avatar = (f'<img src="data:image/png;base64,{img_b64}" alt="Sriram">'
|
| 417 |
+
if img_b64 else
|
| 418 |
+
'<div style="width:72px;height:72px;background:#1f6feb;border-radius:50%;margin:0 auto;"></div>')
|
| 419 |
+
|
| 420 |
+
st.markdown(f"""
|
| 421 |
+
<div class="profile-card">
|
| 422 |
+
<div class="profile-avatar">{avatar}</div>
|
| 423 |
+
<div class="profile-name">SRIRAM SAI</div>
|
| 424 |
+
<div class="profile-role">// AI & ML ENGINEER</div>
|
| 425 |
+
<div class="profile-links">
|
| 426 |
+
<a class="p-link" href="https://github.com/sriramsai18" target="_blank">π» GitHub</a>
|
| 427 |
+
<a class="p-link" href="https://www.linkedin.com/in/sriram-sai-laggisetti/" target="_blank">πΌ LinkedIn</a>
|
| 428 |
+
</div>
|
| 429 |
+
</div>
|
| 430 |
+
""", unsafe_allow_html=True)
|
| 431 |
+
|
| 432 |
+
# PDF upload
|
| 433 |
+
st.markdown('<div style="font-family:\'Share Tech Mono\',monospace;font-size:0.7rem;color:#8b949e;letter-spacing:2px;margin-bottom:8px;">π UPLOAD DOCUMENT</div>', unsafe_allow_html=True)
|
| 434 |
+
|
| 435 |
+
uploaded_file = st.file_uploader(
|
| 436 |
+
"", type=["pdf"],
|
| 437 |
+
label_visibility="collapsed"
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
if uploaded_file:
|
| 441 |
+
if st.session_state.pdf_name != uploaded_file.name:
|
| 442 |
+
with st.spinner("π Processing PDF..."):
|
| 443 |
+
try:
|
| 444 |
+
vs, pages, chunks = process_pdf(uploaded_file)
|
| 445 |
+
st.session_state.vectorstore = vs
|
| 446 |
+
st.session_state.pdf_name = uploaded_file.name
|
| 447 |
+
st.session_state.pdf_pages = pages
|
| 448 |
+
st.session_state.pdf_chunks = chunks
|
| 449 |
+
st.session_state.messages = []
|
| 450 |
+
st.session_state.q_count = 0
|
| 451 |
+
st.success("β
PDF ready!")
|
| 452 |
+
except Exception as e:
|
| 453 |
+
st.error(f"β {str(e)}")
|
| 454 |
+
|
| 455 |
+
st.markdown("---")
|
| 456 |
+
|
| 457 |
+
# Stats
|
| 458 |
+
if st.session_state.pdf_name:
|
| 459 |
+
st.markdown(f"""
|
| 460 |
+
<div style="font-family:'Share Tech Mono',monospace;font-size:0.7rem;color:#8b949e;margin-bottom:10px;letter-spacing:2px;">π DOCUMENT STATS</div>
|
| 461 |
+
<div style="display:flex;flex-direction:column;gap:6px;">
|
| 462 |
+
<div style="background:#0d1117;border:1px solid #21262d;border-radius:6px;padding:8px 12px;font-family:'Share Tech Mono',monospace;font-size:0.7rem;">
|
| 463 |
+
π <span style="color:#c9d1d9;">{st.session_state.pdf_name[:22]}{'...' if len(st.session_state.pdf_name)>22 else ''}</span>
|
| 464 |
+
</div>
|
| 465 |
+
<div style="display:flex;gap:6px;">
|
| 466 |
+
<div style="flex:1;background:#0d1117;border:1px solid #21262d;border-top:2px solid #58a6ff;border-radius:6px;padding:8px;text-align:center;">
|
| 467 |
+
<div style="font-family:'Orbitron',monospace;font-size:1rem;color:#58a6ff;">{st.session_state.pdf_pages}</div>
|
| 468 |
+
<div style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;color:#8b949e;">PAGES</div>
|
| 469 |
+
</div>
|
| 470 |
+
<div style="flex:1;background:#0d1117;border:1px solid #21262d;border-top:2px solid #58a6ff;border-radius:6px;padding:8px;text-align:center;">
|
| 471 |
+
<div style="font-family:'Orbitron',monospace;font-size:1rem;color:#58a6ff;">{st.session_state.pdf_chunks}</div>
|
| 472 |
+
<div style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;color:#8b949e;">CHUNKS</div>
|
| 473 |
+
</div>
|
| 474 |
+
<div style="flex:1;background:#0d1117;border:1px solid #21262d;border-top:2px solid #58a6ff;border-radius:6px;padding:8px;text-align:center;">
|
| 475 |
+
<div style="font-family:'Orbitron',monospace;font-size:1rem;color:#58a6ff;">{st.session_state.q_count}</div>
|
| 476 |
+
<div style="font-family:'Share Tech Mono',monospace;font-size:0.58rem;color:#8b949e;">ASKED</div>
|
| 477 |
+
</div>
|
| 478 |
+
</div>
|
| 479 |
+
</div>
|
| 480 |
+
""", unsafe_allow_html=True)
|
| 481 |
+
st.markdown("---")
|
| 482 |
+
|
| 483 |
+
# Clear button
|
| 484 |
+
if st.button("ποΈ CLEAR CHAT", use_container_width=True):
|
| 485 |
+
st.session_state.messages = []
|
| 486 |
+
st.session_state.q_count = 0
|
| 487 |
+
st.rerun()
|
| 488 |
+
|
| 489 |
+
# New PDF button
|
| 490 |
+
if st.button("π LOAD NEW PDF", use_container_width=True):
|
| 491 |
+
st.session_state.vectorstore = None
|
| 492 |
+
st.session_state.pdf_name = None
|
| 493 |
+
st.session_state.pdf_pages = 0
|
| 494 |
+
st.session_state.pdf_chunks = 0
|
| 495 |
+
st.session_state.messages = []
|
| 496 |
+
st.session_state.q_count = 0
|
| 497 |
+
st.rerun()
|
| 498 |
+
|
| 499 |
+
# βββ MAIN AREA ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 500 |
+
# Header
|
| 501 |
+
st.markdown("""
|
| 502 |
+
<div class="app-header">
|
| 503 |
+
<div>
|
| 504 |
+
<div class="app-title">QUERY<span>DOCS</span> AI π</div>
|
| 505 |
+
<div class="app-sub">// INTELLIGENT DOCUMENT Q&A Β· RAG PIPELINE Β· TINYLLAMA 1.1B</div>
|
| 506 |
+
</div>
|
| 507 |
+
</div>
|
| 508 |
+
""", unsafe_allow_html=True)
|
| 509 |
+
|
| 510 |
+
# PDF banner (when loaded)
|
| 511 |
+
if st.session_state.pdf_name:
|
| 512 |
+
st.markdown(f"""
|
| 513 |
+
<div class="pdf-banner">
|
| 514 |
+
<span style="font-size:1.4rem;">π</span>
|
| 515 |
+
<div>
|
| 516 |
+
<div class="pdf-name">{st.session_state.pdf_name}</div>
|
| 517 |
+
<div class="pdf-meta">{st.session_state.pdf_pages} pages Β· {st.session_state.pdf_chunks} chunks Β· ready to query</div>
|
| 518 |
+
</div>
|
| 519 |
+
<span style="margin-left:auto;background:rgba(88,166,255,0.1);border:1px solid rgba(88,166,255,0.3);
|
| 520 |
+
color:#58a6ff;padding:4px 12px;border-radius:20px;
|
| 521 |
+
font-family:'Share Tech Mono',monospace;font-size:0.65rem;">β ACTIVE</span>
|
| 522 |
+
</div>
|
| 523 |
+
""", unsafe_allow_html=True)
|
| 524 |
+
|
| 525 |
+
# Chat area
|
| 526 |
+
if not st.session_state.vectorstore:
|
| 527 |
+
st.markdown("""
|
| 528 |
+
<div class="welcome-card">
|
| 529 |
+
<div class="wc-icon">π</div>
|
| 530 |
+
<div class="wc-title">WELCOME TO QUERYDOCS AI</div>
|
| 531 |
+
<div class="wc-sub">
|
| 532 |
+
Upload any PDF document from the sidebar and start asking questions.<br>
|
| 533 |
+
Powered by RAG pipeline + TinyLlama 1.1B for accurate, context-aware answers.
|
| 534 |
+
</div>
|
| 535 |
+
<br>
|
| 536 |
+
<span class="tip-chip">π Legal documents</span>
|
| 537 |
+
<span class="tip-chip">π Research papers</span>
|
| 538 |
+
<span class="tip-chip">π Study material</span>
|
| 539 |
+
<span class="tip-chip">π Reports</span>
|
| 540 |
+
</div>
|
| 541 |
+
""", unsafe_allow_html=True)
|
| 542 |
+
|
| 543 |
+
else:
|
| 544 |
+
# Chat history
|
| 545 |
+
if st.session_state.messages:
|
| 546 |
+
chat_html = '<div class="chat-wrap">'
|
| 547 |
+
for msg in st.session_state.messages:
|
| 548 |
+
ts = msg.get("time", "")
|
| 549 |
+
if msg["role"] == "user":
|
| 550 |
+
chat_html += f"""
|
| 551 |
+
<div class="msg-user">
|
| 552 |
+
<div>
|
| 553 |
+
<div class="bubble-user">{msg["content"]}</div>
|
| 554 |
+
<div class="msg-meta" style="text-align:right;">YOU Β· {ts}</div>
|
| 555 |
+
</div>
|
| 556 |
+
</div>"""
|
| 557 |
+
else:
|
| 558 |
+
sources_html = ""
|
| 559 |
+
if msg.get("sources"):
|
| 560 |
+
chips = "".join(f'<span class="source-chip">π Chunk {i+1}</span>'
|
| 561 |
+
for i, _ in enumerate(msg["sources"]))
|
| 562 |
+
sources_html = f"""
|
| 563 |
+
<div class="sources-wrap">
|
| 564 |
+
<div class="source-label">// SOURCE CHUNKS USED</div>
|
| 565 |
+
{chips}
|
| 566 |
+
</div>"""
|
| 567 |
+
chat_html += f"""
|
| 568 |
+
<div class="msg-ai">
|
| 569 |
+
<div>
|
| 570 |
+
<div class="bubble-ai">
|
| 571 |
+
<div class="answer-label">// QUERYDOCS RESPONSE</div>
|
| 572 |
+
{msg["content"]}
|
| 573 |
+
{sources_html}
|
| 574 |
+
</div>
|
| 575 |
+
<div class="msg-meta">π QUERYDOCS AI Β· {ts} Β· {msg.get("elapsed","?")}s</div>
|
| 576 |
+
</div>
|
| 577 |
+
</div>"""
|
| 578 |
+
chat_html += '</div>'
|
| 579 |
+
st.markdown(chat_html, unsafe_allow_html=True)
|
| 580 |
+
else:
|
| 581 |
+
st.markdown("""
|
| 582 |
+
<div class="welcome-card" style="padding:20px;">
|
| 583 |
+
<div style="font-size:1.6rem;margin-bottom:8px;">π¬</div>
|
| 584 |
+
<div class="wc-title" style="font-size:0.85rem;">DOCUMENT LOADED β START ASKING</div>
|
| 585 |
+
<div class="wc-sub" style="font-size:0.82rem;">Ask anything about the uploaded document.</div>
|
| 586 |
+
<br>
|
| 587 |
+
<span class="tip-chip">π‘ Summarize this document</span>
|
| 588 |
+
<span class="tip-chip">π‘ What are the key findings?</span>
|
| 589 |
+
<span class="tip-chip">π‘ List all important dates</span>
|
| 590 |
+
</div>
|
| 591 |
+
""", unsafe_allow_html=True)
|
| 592 |
+
|
| 593 |
+
# Input row
|
| 594 |
+
st.markdown('<hr class="glow-div">', unsafe_allow_html=True)
|
| 595 |
+
col_q, col_btn = st.columns((5, 1))
|
| 596 |
+
|
| 597 |
+
with col_q:
|
| 598 |
+
question = st.text_input(
|
| 599 |
+
"", placeholder="// ask a question about your document...",
|
| 600 |
+
label_visibility="collapsed", key="question_input"
|
| 601 |
+
)
|
| 602 |
+
with col_btn:
|
| 603 |
+
st.markdown('<div class="send-btn">', unsafe_allow_html=True)
|
| 604 |
+
ask_btn = st.button("βΆ ASK", use_container_width=True)
|
| 605 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 606 |
+
|
| 607 |
+
# Generate answer
|
| 608 |
+
if (ask_btn or question) and question.strip():
|
| 609 |
+
ts = time.strftime("%H:%M")
|
| 610 |
+
st.session_state.messages.append({
|
| 611 |
+
"role": "user", "content": question.strip(), "time": ts
|
| 612 |
+
})
|
| 613 |
+
st.session_state.q_count += 1
|
| 614 |
+
|
| 615 |
+
# typing indicator
|
| 616 |
+
typing_slot = st.empty()
|
| 617 |
+
typing_slot.markdown("""
|
| 618 |
+
<div class="typing-wrap">
|
| 619 |
+
<div class="typing-box">
|
| 620 |
+
<div class="t-dot"></div>
|
| 621 |
+
<div class="t-dot"></div>
|
| 622 |
+
<div class="t-dot"></div>
|
| 623 |
+
</div>
|
| 624 |
+
</div>
|
| 625 |
+
""", unsafe_allow_html=True)
|
| 626 |
|
| 627 |
+
try:
|
| 628 |
+
start = time.time()
|
| 629 |
+
answer, sources = get_answer(question.strip(), st.session_state.vectorstore)
|
| 630 |
+
elapsed = round(time.time() - start, 1)
|
| 631 |
|
| 632 |
+
st.session_state.messages.append({
|
| 633 |
+
"role": "assistant",
|
| 634 |
+
"content": answer,
|
| 635 |
+
"sources": sources,
|
| 636 |
+
"time": time.strftime("%H:%M"),
|
| 637 |
+
"elapsed": elapsed
|
| 638 |
+
})
|
| 639 |
+
except Exception as e:
|
| 640 |
+
st.session_state.messages.append({
|
| 641 |
+
"role": "assistant",
|
| 642 |
+
"content": f"β οΈ Error generating answer: {str(e)}",
|
| 643 |
+
"sources": [], "time": time.strftime("%H:%M"), "elapsed": 0
|
| 644 |
+
})
|
| 645 |
|
| 646 |
+
typing_slot.empty()
|
| 647 |
+
st.rerun()
|