Sathvik-kota commited on
Commit
554cd24
·
verified ·
1 Parent(s): 90b392b

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. src/ui/streamlit_app.py +259 -80
src/ui/streamlit_app.py CHANGED
@@ -29,43 +29,144 @@ API_GATEWAY_URL = "http://localhost:8000"
29
  st.set_page_config(
30
  page_title="Doc-Fetch",
31
  layout="wide",
32
- initial_sidebar_state="expanded"
33
  )
34
 
35
- # =======================
36
- # Disable Autocomplete
37
- # =======================
38
- st.markdown("""
39
- <style>
40
- input[type=text] {
41
- autocomplete: off !important;
42
- }
43
- </style>
44
-
45
- <script>
46
- document.addEventListener('DOMContentLoaded', function() {
47
- const inputs = window.parent.document.querySelectorAll('input[type="text"]');
48
- inputs.forEach(inp => {
49
- inp.setAttribute('autocomplete', 'off');
50
- inp.setAttribute('autocorrect', 'off');
51
- inp.setAttribute('autocapitalize', 'off');
52
- inp.setAttribute('spellcheck', 'false');
53
- });
54
- });
55
- </script>
56
- """, unsafe_allow_html=True)
57
-
58
  # =======================
59
  # GEMINI UI STYLING
60
  # =======================
61
  st.markdown("""
62
  <style>
63
- /* Your entire CSS EXACTLY as earlier (not reprinting for brevity) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  </style>
65
  """, unsafe_allow_html=True)
66
 
67
  # =======================
68
- # SIDEBAR
69
  # =======================
70
  with st.sidebar:
71
  st.markdown("### ⚙️ Settings")
@@ -75,58 +176,46 @@ with st.sidebar:
75
  st.subheader(" Evaluation")
76
  run_eval = st.button("Run Evaluation Script")
77
  st.divider()
78
- st.caption("Semantic Search · Smart Cache · FAISS Retrieval · Multi-Service Architecture · Relevance by MiniLM Embeddings · Reasoning by LLM.")
79
 
80
  API_GATEWAY_URL = url_input
81
 
82
  # =======================
83
- # MAIN HEADER
84
  # =======================
85
  col1, col2, col3 = st.columns([1, 6, 1])
86
  with col2:
87
- st.markdown('<div style="text-align:center;margin-bottom:10px;"><span class="gradient-text">Hello, Explorer</span></div>', unsafe_allow_html=True)
88
- st.markdown('<div style="text-align:center;color:#444746;font-size:1.2rem;margin-bottom:30px;">How can I help you find documents today?</div>', unsafe_allow_html=True)
 
89
 
90
 
91
  # =======================
92
- # SEARCH INPUT (ENTER TRIGGER)
93
  # =======================
94
-
95
- # SESSION STATE to detect Enter key automatically
96
- if "trigger_enter" not in st.session_state:
97
- st.session_state["trigger_enter"] = False
98
-
99
- def enter_pressed():
100
- st.session_state["trigger_enter"] = True
101
-
102
- sc1, sc2, sc3 = st.columns([1,4,1])
103
 
104
  with sc2:
105
  query = st.text_input(
106
- "Search Query",
107
  placeholder="Ask a question about your documents...",
108
- label_visibility="collapsed",
109
- key="search_box",
110
- on_change=enter_pressed # <-- ENTER activates search
111
  )
112
-
113
  # Buttons row
114
- b1, b2, b3 = st.columns([2,1,2])
115
  with b2:
116
  submit_btn = st.button("Sparkle Search", type="primary", use_container_width=True)
117
 
118
- # TRUE if button clicked OR Enter pressed
119
- trigger_search = submit_btn or st.session_state["trigger_enter"]
120
-
121
  # =======================
122
  # SEARCH HANDLER
123
  # =======================
124
- if trigger_search and query.strip():
125
-
126
- # Reset enter state
127
- st.session_state["trigger_enter"] = False
128
 
 
129
  with st.spinner("✨ Analyzing semantics..."):
 
130
  response = requests.post(
131
  f"{API_GATEWAY_URL}/search",
132
  json={"query": query, "top_k": top_k}
@@ -146,10 +235,13 @@ if trigger_search and query.strip():
146
  st.info("No relevant documents found for that query.")
147
  st.stop()
148
 
149
- # Search Results
150
  st.markdown("### ✨ Search Results")
151
  st.markdown("---")
152
 
 
 
 
153
  for item in data["results"]:
154
  filename = item["filename"]
155
  score = item["score"]
@@ -159,69 +251,156 @@ if trigger_search and query.strip():
159
 
160
  safe_preview = html.escape(preview)
161
 
 
162
  keywords = explanation.get("keyword_overlap", [])
163
- keyword_html = "".join([f"<span class='keyword-pill'>{kw}</span>" for kw in keywords])
164
-
165
- doc_icon = """<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0b57d0" stroke-width="2"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>"""
 
 
 
166
 
 
167
  st.markdown(f"""
168
  <div class="result-card">
169
- <div style="display:flex;justify-content:space-between;align-items:start;">
170
- <div class="card-title">{doc_icon} {filename}</div>
 
 
171
  <div class="score-badge">match: {score:.4f}</div>
172
  </div>
173
  <p class="card-preview">{safe_preview}...</p>
174
- <div style="margin-top:10px;">
175
- <div style="font-weight:600;color:#1f1f1f;margin-bottom:6px;">Keyword Overlap:</div>
 
 
176
  {keyword_html}
177
  </div>
178
  </div>
179
  """, unsafe_allow_html=True)
180
 
 
181
  with st.expander(f"View Document Insights: Semantic Overlap, Top Sentences, LLM Reasoning & Full Text for {filename}"):
 
182
  overlap_ratio = explanation.get("overlap_ratio", 0)
183
  sentences = explanation.get("top_sentences", [])
184
 
185
  st.caption(f"Semantic Overlap Ratio: {overlap_ratio:.3f}")
186
-
187
  if sentences:
188
  st.markdown("**Key Excerpts:**")
189
  for s in sentences:
 
190
  st.markdown(f"""
191
- <div style="background:#fff;border-left:3px solid #4285f4;padding:10px;margin-bottom:5px;border-radius:0 8px 8px 0;">
192
- "{s['sentence']}" <span style="color:#5e5e5e;font-size:0.8em;">(conf: {s['score']:.2f})</span>
 
193
  </div>
194
  """, unsafe_allow_html=True)
195
-
196
  llm_expl = explanation.get("llm_explanation")
197
  if llm_expl:
198
- st.markdown("**Why this document?**")
199
- st.write(llm_expl)
200
-
201
  st.markdown("---")
202
  st.markdown("**📄 Full Document Content:**")
203
- st.code(full_text, language="text")
204
-
205
-
206
- # =======================
207
- # EVALUATION MODULE
208
- # =======================
209
  if run_eval:
210
 
211
  st.info("Running evaluation... this may take 10–20 seconds...")
 
212
  results = run_evaluation(top_k=10)
 
213
  st.success("Evaluation Complete!")
214
 
215
- st.markdown("## Evaluation Summary")
 
 
 
216
 
217
  c1, c2, c3, c4 = st.columns(4)
218
- with c1: st.metric("Accuracy", f"{results['accuracy']}%")
219
- with c2: st.metric("MRR", results["mrr"])
220
- with c3: st.metric("NDCG", results["ndcg"])
221
- with c4: st.metric("Queries", results["total_queries"])
 
 
 
 
222
 
223
  st.markdown(
224
- f"**Correct:** {results['correct_count']} | **Incorrect:** {results['incorrect_count']}"
 
225
  )
226
 
227
  st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  st.set_page_config(
30
  page_title="Doc-Fetch",
31
  layout="wide",
32
+ initial_sidebar_state="expanded", # Changed from "collapsed" to "expanded"
33
  )
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  # =======================
36
  # GEMINI UI STYLING
37
  # =======================
38
  st.markdown("""
39
  <style>
40
+ /* Global Font & Background */
41
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
42
+
43
+ html, body, [class*="css"] {
44
+ font-family: 'Inter', sans-serif;
45
+ background-color: #ffffff; /* White Background */
46
+ color: #1f1f1f; /* Dark text for contrast */
47
+ }
48
+
49
+ /* --- INPUT FIELD FIX --- */
50
+ /* 1. Remove the default Streamlit border/background on the container */
51
+ .stTextInput > div[data-baseweb="input"] {
52
+ background-color: transparent !important;
53
+ border: none !important;
54
+ border-radius: 24px !important;
55
+ box-shadow: none !important;
56
+ }
57
+
58
+ /* 2. Style the actual input element */
59
+ .stTextInput input {
60
+ border-radius: 24px !important;
61
+ background-color: #f0f4f9 !important; /* Light ash input */
62
+ border: 1px solid transparent !important;
63
+ color: #1f1f1f !important;
64
+ padding: 12px 20px !important;
65
+ font-size: 16px !important;
66
+ transition: all 0.2s ease;
67
+ }
68
+
69
+ /* 3. Focus state - clean blue border, no default red overlay */
70
+ .stTextInput input:focus {
71
+ background-color: #ffffff !important;
72
+ border-color: #0b57d0 !important; /* Gemini Blue */
73
+ box-shadow: 0 0 0 2px rgba(11, 87, 208, 0.2) !important;
74
+ outline: none !important;
75
+ }
76
+
77
+ /* Button Styling */
78
+ .stButton > button {
79
+ border-radius: 20px;
80
+ font-weight: 500;
81
+ border: none;
82
+ padding: 0.5rem 1.5rem;
83
+ transition: all 0.3s ease;
84
+ white-space: nowrap; /* Forces text to stay on one line */
85
+ min-width: 140px; /* Ensures button is never too skinny */
86
+ }
87
+
88
+ /* Primary Search Button */
89
+ button[kind="primary"] {
90
+ background: linear-gradient(90deg, #4b90ff, #ff5546);
91
+ color: white;
92
+ }
93
+ button[kind="primary"]:hover {
94
+ opacity: 0.9;
95
+ box-shadow: 0 4px 12px rgba(75, 144, 255, 0.3);
96
+ }
97
+
98
+ /* Result Card - Light Ash Background */
99
+ .result-card {
100
+ background-color: #f0f4f9; /* Light Ash */
101
+ border-radius: 16px;
102
+ padding: 1.5rem;
103
+ margin-bottom: 1rem;
104
+ border: none; /* Removed border for cleaner look on light mode */
105
+ transition: transform 0.2s;
106
+ }
107
+ .result-card:hover {
108
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
109
+ }
110
+
111
+ /* Typography in Cards */
112
+ .card-title {
113
+ color: #1f1f1f; /* Dark Title */
114
+ font-size: 1.1rem;
115
+ font-weight: 600;
116
+ margin-bottom: 0.5rem;
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 8px;
120
+ }
121
+
122
+ .card-preview {
123
+ color: #444746; /* Darker gray for readable preview */
124
+ font-size: 0.95rem;
125
+ line-height: 1.5;
126
+ margin-bottom: 1rem;
127
+ }
128
+
129
+ /* Pills & Badges */
130
+ .score-badge {
131
+ background-color: #c4eed0; /* Light Green bg */
132
+ color: #0f5223; /* Dark Green text */
133
+ padding: 4px 12px;
134
+ border-radius: 12px;
135
+ font-size: 0.75rem;
136
+ font-weight: 500;
137
+ display: inline-block;
138
+ }
139
+
140
+ .keyword-pill {
141
+ background-color: #c2e7ff; /* Light Blue bg */
142
+ color: #004a77; /* Dark Blue text */
143
+ padding: 2px 10px;
144
+ border-radius: 8px;
145
+ font-size: 0.8rem;
146
+ margin-right: 6px;
147
+ display: inline-block;
148
+ margin-bottom: 4px;
149
+ }
150
+
151
+ /* Gradient Text for Header */
152
+ .gradient-text {
153
+ background: linear-gradient(to right, #4285f4, #9b72cb, #d96570);
154
+ -webkit-background-clip: text;
155
+ -webkit-text-fill-color: transparent;
156
+ font-weight: 700;
157
+ font-size: 3rem;
158
+ }
159
+
160
+ /* Custom Info Box */
161
+ .stAlert {
162
+ background-color: #f0f4f9;
163
+ color: #1f1f1f;
164
+ }
165
  </style>
166
  """, unsafe_allow_html=True)
167
 
168
  # =======================
169
+ # SIDEBAR (Settings)
170
  # =======================
171
  with st.sidebar:
172
  st.markdown("### ⚙️ Settings")
 
176
  st.subheader(" Evaluation")
177
  run_eval = st.button("Run Evaluation Script")
178
  st.divider()
179
+ st.caption("Semantic Search · Smart Cache · FAISS Retrieval · Multi-Service Architecture · Relevance by MiniLM Embeddings. Reasoning by LLM.")
180
 
181
  API_GATEWAY_URL = url_input
182
 
183
  # =======================
184
+ # MAIN HEADER (Gemini Style)
185
  # =======================
186
  col1, col2, col3 = st.columns([1, 6, 1])
187
  with col2:
188
+ # Use HTML for the gradient text title
189
+ st.markdown('<div style="text-align: center; margin-bottom: 10px;"><span class="gradient-text">Hello, Explorer</span></div>', unsafe_allow_html=True)
190
+ st.markdown('<div style="text-align: center; color: #444746; font-size: 1.2rem; margin-bottom: 30px;">How can I help you find documents today?</div>', unsafe_allow_html=True)
191
 
192
 
193
  # =======================
194
+ # SEARCH BAR CENTERED
195
  # =======================
196
+ # Centering the search bar using columns
197
+ sc1, sc2, sc3 = st.columns([1, 4, 1])
 
 
 
 
 
 
 
198
 
199
  with sc2:
200
  query = st.text_input(
201
+ "Search Query", # Label hidden by CSS/Config if needed, or set visibility hidden
202
  placeholder="Ask a question about your documents...",
203
+ label_visibility="collapsed"
 
 
204
  )
205
+
206
  # Buttons row
207
+ b1, b2, b3 = st.columns([2, 1, 2])
208
  with b2:
209
  submit_btn = st.button("Sparkle Search", type="primary", use_container_width=True)
210
 
 
 
 
211
  # =======================
212
  # SEARCH HANDLER
213
  # =======================
214
+ if submit_btn and query.strip():
 
 
 
215
 
216
+ # Gemini-style spinner
217
  with st.spinner("✨ Analyzing semantics..."):
218
+
219
  response = requests.post(
220
  f"{API_GATEWAY_URL}/search",
221
  json={"query": query, "top_k": top_k}
 
235
  st.info("No relevant documents found for that query.")
236
  st.stop()
237
 
238
+ # Results Header
239
  st.markdown("### ✨ Search Results")
240
  st.markdown("---")
241
 
242
+ # =======================
243
+ # DISPLAY RESULTS (Card Style)
244
+ # =======================
245
  for item in data["results"]:
246
  filename = item["filename"]
247
  score = item["score"]
 
251
 
252
  safe_preview = html.escape(preview)
253
 
254
+ # Prepare keyword HTML
255
  keywords = explanation.get("keyword_overlap", [])
256
+ keyword_html = ""
257
+ if keywords:
258
+ keyword_html = "".join([f"<span class='keyword-pill'>{kw}</span>" for kw in keywords])
259
+
260
+ # Doc Icon (SVG) - Changed stroke to dark blue for visibility on light bg
261
+ doc_icon = """<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0b57d0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>"""
262
 
263
+ # Main Card Render
264
  st.markdown(f"""
265
  <div class="result-card">
266
+ <div style="display:flex; justify-content:space-between; align-items:start;">
267
+ <div class="card-title">
268
+ {doc_icon} {filename}
269
+ </div>
270
  <div class="score-badge">match: {score:.4f}</div>
271
  </div>
272
  <p class="card-preview">{safe_preview}...</p>
273
+ <div style="margin-top: 10px;">
274
+ <div style="font-weight:600; color:#1f1f1f; margin-bottom:6px;">
275
+ Keyword Overlap:
276
+ </div>
277
  {keyword_html}
278
  </div>
279
  </div>
280
  """, unsafe_allow_html=True)
281
 
282
+ # Details Expander (Standard Streamlit but styled via global CSS)
283
  with st.expander(f"View Document Insights: Semantic Overlap, Top Sentences, LLM Reasoning & Full Text for {filename}"):
284
+
285
  overlap_ratio = explanation.get("overlap_ratio", 0)
286
  sentences = explanation.get("top_sentences", [])
287
 
288
  st.caption(f"Semantic Overlap Ratio: {overlap_ratio:.3f}")
289
+
290
  if sentences:
291
  st.markdown("**Key Excerpts:**")
292
  for s in sentences:
293
+ # Updated quote box for light mode
294
  st.markdown(f"""
295
+ <div style="background: #ffffff; border-left: 3px solid #4285f4; padding: 10px; margin-bottom: 5px; border-radius: 0 8px 8px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
296
+ <span style="color: #1f1f1f;">"{s['sentence']}"</span>
297
+ <span style="color: #5e5e5e; font-size: 0.8em; margin-left: 10px;">(conf: {s['score']:.2f})</span>
298
  </div>
299
  """, unsafe_allow_html=True)
 
300
  llm_expl = explanation.get("llm_explanation")
301
  if llm_expl:
302
+ st.markdown("**Why this document?**")
303
+ st.write(llm_expl)
 
304
  st.markdown("---")
305
  st.markdown("**📄 Full Document Content:**")
306
+ st.code(full_text, language="text") # Using code block for better readability of raw text
 
 
 
 
 
307
  if run_eval:
308
 
309
  st.info("Running evaluation... this may take 10–20 seconds...")
310
+
311
  results = run_evaluation(top_k=10)
312
+
313
  st.success("Evaluation Complete!")
314
 
315
+ # -----------------------------
316
+ # Summary Metrics (Horizontal)
317
+ # -----------------------------
318
+ st.markdown("## Evaluation Summary")
319
 
320
  c1, c2, c3, c4 = st.columns(4)
321
+ with c1:
322
+ st.metric("Accuracy", f"{results['accuracy']}%")
323
+ with c2:
324
+ st.metric("MRR", results["mrr"])
325
+ with c3:
326
+ st.metric("NDCG", results["ndcg"])
327
+ with c4:
328
+ st.metric("Queries", results["total_queries"])
329
 
330
  st.markdown(
331
+ f"**Correct:** {results['correct_count']} &nbsp;&nbsp;|&nbsp;&nbsp; "
332
+ f"**Incorrect:** {results['incorrect_count']}"
333
  )
334
 
335
  st.markdown("---")
336
+
337
+ # -----------------------------
338
+ # Incorrect Results
339
+ # -----------------------------
340
+ st.markdown("## Incorrect Fetches ")
341
+
342
+ wrong = [d for d in results["details"] if not d["is_correct"]]
343
+
344
+ if wrong:
345
+ for item in wrong:
346
+ st.markdown(f"""
347
+ <div style="
348
+ padding:14px;
349
+ background:#ffe5e5;
350
+ border-left:5px solid #ff4d4f;
351
+ border-radius:8px;
352
+ margin-bottom:10px;">
353
+ <b> Query:</b> {item['query']}<br>
354
+ <b>Expected:</b> {item['expected']}<br>
355
+ <b>Retrieved:</b> {item['retrieved']}<br>
356
+ <b>Rank:</b> {item['rank']}
357
+ </div>
358
+ """, unsafe_allow_html=True)
359
+ else:
360
+ st.success(" No incorrect queries!")
361
+
362
+ st.markdown("---")
363
+
364
+ # -----------------------------
365
+ # Correct Results
366
+ # -----------------------------
367
+ st.markdown("## Correct Fetches")
368
+
369
+ correct_items = [d for d in results["details"] if d["is_correct"]]
370
+
371
+ if correct_items:
372
+ for item in correct_items:
373
+ st.markdown(f"""
374
+ <div style="
375
+ padding:14px;
376
+ background:#e8ffe5;
377
+ border-left:5px solid #2ecc71;
378
+ border-radius:8px;
379
+ margin-bottom:10px;">
380
+ <b> Query:</b> {item['query']}<br>
381
+ <b>Expected:</b> {item['expected']}<br>
382
+ <b>Top-K Retrieved:</b> {item['retrieved']}<br>
383
+ <b>Rank:</b> {item['rank']}
384
+ </div>
385
+ """, unsafe_allow_html=True)
386
+ else:
387
+ st.info("No correct queries.")
388
+
389
+ st.markdown("---")
390
+
391
+ # -----------------------------
392
+ # Full Table
393
+ # -----------------------------
394
+ st.markdown("## Full Evaluation Table")
395
+
396
+ table_data = []
397
+ for item in results["details"]:
398
+ table_data.append({
399
+ "Query": item["query"],
400
+ "Expected Doc": item["expected"],
401
+ "Retrieved (Top-10)": ", ".join(item["retrieved"]),
402
+ "Correct?": "Yes" if item["is_correct"] else "No",
403
+ "Rank": item["rank"]
404
+ })
405
+
406
+ st.dataframe(table_data, use_container_width=True)