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

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. src/ui/streamlit_app.py +80 -259
src/ui/streamlit_app.py CHANGED
@@ -29,144 +29,43 @@ API_GATEWAY_URL = "http://localhost:8000"
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,46 +75,58 @@ with st.sidebar:
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,13 +146,10 @@ if submit_btn and query.strip():
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,156 +159,69 @@ if submit_btn and query.strip():
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)
 
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
  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
  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
 
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("---")