vikramronavrsc commited on
Commit
15f0c95
·
verified ·
1 Parent(s): 6d9e7a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +521 -291
app.py CHANGED
@@ -1,13 +1,172 @@
1
- # app.py
2
  import os
3
  import shutil
4
  import streamlit as st
5
  import torch
6
  import atexit
 
7
  from advanced_rag import AdvancedRAG
8
  from metamask_component import metamask_connector
9
  from voice_component import voice_input_component
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  # Helper function to initialize session state
12
  def initialize_session_state():
13
  """Initialize Streamlit session state variables."""
@@ -23,6 +182,8 @@ def initialize_session_state():
23
  st.session_state.retrieval_method = "enhanced"
24
  if "voice_transcript" not in st.session_state:
25
  st.session_state.voice_transcript = ""
 
 
26
 
27
  # Helper function to clean up temporary files
28
  def cleanup_temp_files():
@@ -34,330 +195,399 @@ def cleanup_temp_files():
34
  except Exception as e:
35
  print(f"Error cleaning up temporary directory: {e}")
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # Streamlit UI
38
  def main():
39
  st.set_page_config(
40
- page_title="Advanced RAG with MetaMask and Voice",
41
  layout="wide",
42
  initial_sidebar_state="expanded"
43
  )
44
 
45
- st.title("🚀 Advanced RAG System with Blockchain Verification and Voice Input")
46
- st.markdown("""
47
- This application allows you to:
48
- - Upload and process PDF documents
49
- - Verify document authenticity on blockchain using MetaMask
50
- - Ask questions using voice or text input
51
- - Choose between direct retrieval or enhanced LLM-powered answers
52
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  # Initialize session state
55
  initialize_session_state()
56
 
57
- # MetaMask Connection Section
58
- st.header("🦊 MetaMask Connection")
59
- st.markdown("Connect your MetaMask wallet to verify documents and log queries on the blockchain.")
60
-
61
- # Add MetaMask connector and get connection info
62
- metamask_info = metamask_connector()
63
-
64
- # Display MetaMask connection status
65
- if metamask_info and metamask_info.get("connected"):
66
- st.success(f"✅ MetaMask Connected: {metamask_info.get('address')}")
67
- st.info(f"Network: {metamask_info.get('network_name')}")
68
- st.session_state.metamask_connected = True
69
- else:
70
- st.warning("⚠️ MetaMask not connected. Please connect your wallet to use blockchain features.")
71
- st.session_state.metamask_connected = False
72
-
73
- # Update RAG system with MetaMask connection if needed
74
- if st.session_state.rag and metamask_info:
75
- is_connected = st.session_state.rag.update_blockchain_connection(metamask_info)
76
- if is_connected:
77
- st.success("RAG system updated with MetaMask connection")
78
-
79
  # Sidebar for configuration and file upload
80
  with st.sidebar:
81
- st.header("⚙️ Configuration")
82
-
83
- # GPU Detection
84
- gpu_available = torch.cuda.is_available()
85
- if gpu_available:
86
- try:
87
- gpu_info = torch.cuda.get_device_properties(0)
88
- st.success(f"GPU detected: {gpu_info.name} ({gpu_info.total_memory / 1024**3:.1f} GB)")
89
- except Exception as e:
90
- st.warning(f"GPU detected but couldn't get properties: {str(e)}")
91
- st.info("Running with limited GPU information")
92
- else:
93
- st.warning("No GPU detected. Running in CPU mode.")
94
-
95
- # Model selection
96
- st.subheader("Model Selection")
97
- llm_model = st.selectbox(
98
- "LLM Model",
99
- options=[
100
- "mistralai/Mistral-7B-Instruct-v0.2",
101
- "meta-llama/Llama-3.2-1B",
102
- "microsoft/Phi-4-mini-instruct",
103
- "Salesforce/xgen-7b-8k-inst",
104
- "meta-llama/Llama-3.2-3B-Instruct"
105
- ],
106
- index=0
107
- )
108
-
109
- embedding_model = st.selectbox(
110
- "Embedding Model",
111
- options=[
112
- "sentence-transformers/all-mpnet-base-v2",
113
- "sentence-transformers/all-MiniLM-L6-v2",
114
- "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
115
- ],
116
- index=1 # all-MiniLM-L6-v2 is smaller and faster
117
- )
118
 
119
- use_gpu = st.checkbox("Use GPU Acceleration", value=gpu_available)
120
-
121
- # Blockchain configuration
122
- st.subheader("🔗 Blockchain Configuration")
123
- use_blockchain = st.checkbox("Enable Blockchain Verification", value=True)
124
-
125
- if use_blockchain:
126
- # Hardcoded contract address - replace with your deployed contract
127
- contract_address = os.environ.get("CONTRACT_ADDRESS", "0x123abc...") # Your pre-deployed contract
128
 
129
- st.info(f"Using pre-deployed contract: {contract_address[:10]}...")
 
130
 
131
- # Display MetaMask connection status in sidebar
132
  if metamask_info and metamask_info.get("connected"):
133
- st.success(f"✅ MetaMask Connected: {metamask_info.get('address')[:10]}...")
 
 
134
  else:
135
- st.warning("⚠️ MetaMask not connected. Please connect your wallet above.")
136
-
137
- # Advanced options
138
- with st.expander("Advanced Options"):
139
- chunk_size = st.slider("Chunk Size", 100, 2000, 1000)
140
- chunk_overlap = st.slider("Chunk Overlap", 0, 500, 200)
141
-
142
- # Initialize button
143
- if st.button("Initialize System"):
144
- with st.spinner("Initializing RAG system..."):
145
- if use_blockchain and not contract_address:
146
- st.error("Contract address is required for blockchain integration")
147
- else:
148
- st.session_state.rag = AdvancedRAG(
149
- llm_model_name=llm_model,
150
- embedding_model_name=embedding_model,
151
- chunk_size=chunk_size,
152
- chunk_overlap=chunk_overlap,
153
- use_gpu=use_gpu and gpu_available,
154
- use_blockchain=use_blockchain,
155
- contract_address=contract_address if use_blockchain else None
156
- )
157
-
158
- # Update with current MetaMask connection if available
159
- if use_blockchain and metamask_info:
160
- st.session_state.rag.update_blockchain_connection(metamask_info)
161
-
162
- st.success(f"System initialized with {embedding_model} on {st.session_state.rag.device}")
163
- if use_blockchain:
164
- if metamask_info and metamask_info.get("connected"):
165
- st.success("Blockchain verification enabled with MetaMask")
166
- else:
167
- st.warning("Blockchain verification enabled but MetaMask not connected")
168
-
169
- st.header("📄 Upload Documents")
170
- uploaded_files = st.file_uploader("Select PDFs", type="pdf", accept_multiple_files=True)
171
 
172
- if uploaded_files and st.button("Process PDFs"):
173
- if not st.session_state.rag:
174
- with st.spinner("Initializing RAG system..."):
175
- st.session_state.rag = AdvancedRAG(
176
- llm_model_name=llm_model,
177
- embedding_model_name=embedding_model,
178
- chunk_size=chunk_size,
179
- chunk_overlap=chunk_overlap,
180
- use_gpu=use_gpu and gpu_available,
181
- use_blockchain=use_blockchain,
182
- contract_address=contract_address if use_blockchain else None
183
- )
184
-
185
- # Update with current MetaMask connection if available
186
- if use_blockchain and metamask_info:
187
- st.session_state.rag.update_blockchain_connection(metamask_info)
188
 
189
- success = st.session_state.rag.process_pdfs(uploaded_files)
190
- if success:
191
- metrics = st.session_state.rag.get_performance_metrics()
192
- if metrics:
193
- st.success("PDFs processed successfully!")
194
- with st.expander("💹 Performance Metrics"):
195
- st.markdown(f"**Documents processed:** {metrics['documents_processed']} chunks")
196
- st.markdown(f"**Index building time:** {metrics['index_building_time']:.2f} seconds")
197
- st.markdown(f"**Total processing time:** {metrics['total_processing_time']:.2f} seconds")
198
- st.markdown(f"**Memory used:** {metrics['memory_used_gb']:.2f} GB")
199
- st.markdown(f"**Device used:** {metrics['device']}")
200
- st.markdown(f"**Blockchain verification:** {'Enabled' if metrics['blockchain_enabled'] else 'Disabled'}")
201
- st.markdown(f"**Blockchain connected:** {'Yes' if metrics.get('blockchain_connected') else 'No'}")
202
-
203
- # Retrieval Method Selection
204
- st.header("🔍 Retrieval Method")
205
- retrieval_cols = st.columns(2)
206
-
207
- with retrieval_cols[0]:
208
- if st.button("📄 Direct Retrieval", help="Get raw document chunks without LLM processing", use_container_width=True):
209
- st.session_state.retrieval_method = "direct"
210
- st.info("Using Direct Retrieval: Raw document passages will be returned without LLM processing")
211
 
212
- with retrieval_cols[1]:
213
- if st.button("🧠 Enhanced Retrieval", help="Process results through LLM for comprehensive answers", use_container_width=True):
214
- st.session_state.retrieval_method = "enhanced"
215
- st.info("Using Enhanced Retrieval: Documents will be processed by LLM to generate comprehensive answers")
216
-
217
- # Blockchain verification info
218
- if st.session_state.rag and st.session_state.rag.use_blockchain:
219
- if st.session_state.metamask_connected:
220
- st.info("🔗 Blockchain verification is enabled with MetaMask. Documents are cryptographically verified and queries are logged with immutable audit trail.")
221
- else:
222
- st.warning("🔗 Blockchain verification is enabled but MetaMask is not connected. Please connect your MetaMask wallet to use blockchain features.")
223
-
224
- # Display chat messages
225
- st.header("💬 Chat")
226
-
227
- # Chat container
228
- chat_container = st.container(height=400, border=True)
229
-
230
- with chat_container:
231
- for message in st.session_state.messages:
232
- with st.chat_message(message["role"]):
233
- if message["role"] == "user":
234
- st.markdown(message["content"])
235
- else:
236
- if isinstance(message["content"], dict):
237
- st.markdown(message["content"]["answer"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- if "query_time" in message["content"]:
240
- st.caption(f"Response time: {message['content']['query_time']:.2f} seconds")
 
241
 
242
- if "method" in message["content"]:
243
- method_name = "Direct Retrieval" if message["content"]["method"] == "direct" else "Enhanced Retrieval"
244
- st.caption(f"Method: {method_name}")
 
 
 
 
 
 
 
 
245
 
246
- # Display blockchain log if available
247
- if "blockchain_log" in message["content"] and message["content"]["blockchain_log"]:
248
- blockchain_log = message["content"]["blockchain_log"]
249
- st.success(f"✅ Query logged on blockchain | Transaction: {blockchain_log['tx_hash'][:10]}...")
 
 
 
 
 
250
 
251
- # Display sources in expander
252
- if "sources" in message["content"] and message["content"]["sources"]:
253
- with st.expander("📄 View Sources"):
254
- for i, source in enumerate(message["content"]["sources"]):
255
- st.markdown(f"**Source {i+1}: {source['source']}**")
256
-
257
- # Show blockchain verification if available
258
- if source.get("blockchain"):
259
- st.success(f"✅ Verified on blockchain | TX: {source['blockchain']['tx_hash'][:10]}...")
260
-
261
- st.text(source["content"])
262
- st.divider()
263
- else:
264
- st.markdown(message["content"])
265
-
266
- # Voice Input Section
267
- st.header("🎤 Voice Input")
268
- st.markdown("You can ask questions using your voice or type them below.")
269
-
270
- # Voice input component
271
- voice_transcript = voice_input_component()
272
-
273
- # Update session state with voice transcript if not empty
274
- if voice_transcript and voice_transcript.strip():
275
- st.session_state.voice_transcript = voice_transcript.strip()
276
- st.success(f"Voice input received: {voice_transcript}")
277
-
278
- # Chat input - show the voice transcript in the text input
279
- prompt_placeholder = "Ask a question about your PDFs..."
280
- if st.session_state.voice_transcript:
281
- prompt_placeholder = st.session_state.voice_transcript
282
 
283
- # Chat input
284
- prompt = st.chat_input(prompt_placeholder)
285
 
286
- # Process either voice input or text input
287
- if prompt or st.session_state.voice_transcript:
288
- # Prioritize text input over voice input
289
- if prompt:
290
- user_input = prompt
291
- else:
292
- user_input = st.session_state.voice_transcript
293
- # Clear voice transcript after using it
294
- st.session_state.voice_transcript = ""
295
- # Rerun to clear the voice input display
296
- st.rerun()
297
-
298
- # Add user message to chat
299
- st.session_state.messages.append({"role": "user", "content": user_input})
300
-
301
- # Display user message
302
- with chat_container:
303
- with st.chat_message("user"):
304
- st.markdown(user_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
- # Check if system is initialized
307
- if not st.session_state.rag:
308
- with chat_container:
309
- with st.chat_message("assistant"):
310
- message = "Please initialize the system and process PDFs first."
311
- st.markdown(message)
312
- st.session_state.messages.append({"role": "assistant", "content": message})
 
 
313
 
314
- # Get response if vector store is ready
315
- elif st.session_state.rag.vector_store:
316
- with chat_container:
317
- with st.chat_message("assistant"):
318
- # Get retrieval method
319
- method = st.session_state.retrieval_method
320
-
321
- # Get response using specified method
322
- response = st.session_state.rag.ask(user_input, method=method)
323
- st.session_state.messages.append({"role": "assistant", "content": response})
324
-
325
- if isinstance(response, dict):
326
- st.markdown(response["answer"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
 
328
- if "query_time" in response:
329
- st.caption(f"Response time: {response['query_time']:.2f} seconds")
330
 
331
- if "method" in response:
332
- method_name = "Direct Retrieval" if response["method"] == "direct" else "Enhanced Retrieval"
333
- st.caption(f"Method: {method_name}")
334
 
335
- # Display blockchain log if available
336
- if "blockchain_log" in response and response["blockchain_log"]:
337
- blockchain_log = response["blockchain_log"]
338
- st.success(f"✅ Query logged on blockchain | Transaction: {blockchain_log['tx_hash'][:10]}...")
339
 
340
- # Display sources in expander
341
- if "sources" in response and response["sources"]:
342
- with st.expander("📄 View Sources"):
343
- for i, source in enumerate(response["sources"]):
344
- st.markdown(f"**Source {i+1}: {source['source']}**")
345
-
346
- # Show blockchain verification if available
347
- if source.get("blockchain"):
348
- st.success(f"✅ Verified on blockchain | TX: {source['blockchain']['tx_hash'][:10]}...")
349
-
350
- st.text(source["content"])
351
- st.divider()
352
- else:
353
- st.markdown(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  else:
355
- with chat_container:
356
- with st.chat_message("assistant"):
357
- message = "Please upload and process PDF files first."
358
- st.markdown(message)
359
- st.session_state.messages.append({"role": "assistant", "content": message})
360
-
 
 
 
361
 
362
  # Main entry point
363
  if __name__ == "__main__":
 
1
+ # app.py - Enhanced UI with animations
2
  import os
3
  import shutil
4
  import streamlit as st
5
  import torch
6
  import atexit
7
+ import time
8
  from advanced_rag import AdvancedRAG
9
  from metamask_component import metamask_connector
10
  from voice_component import voice_input_component
11
 
12
+ # Custom CSS for enhanced UI
13
+ def load_custom_css():
14
+ st.markdown("""
15
+ <style>
16
+ /* Main container styling */
17
+ .main {
18
+ background-color: #f9fafb;
19
+ }
20
+
21
+ /* Card styling */
22
+ .stCard {
23
+ border-radius: 12px !important;
24
+ box-shadow: 0 6px 16px rgba(0,0,0,0.05) !important;
25
+ transition: all 0.3s ease !important;
26
+ }
27
+ .stCard:hover {
28
+ transform: translateY(-2px);
29
+ box-shadow: 0 12px 24px rgba(0,0,0,0.08) !important;
30
+ }
31
+
32
+ /* Chat message styling */
33
+ .chat-message {
34
+ padding: 16px;
35
+ border-radius: 12px;
36
+ margin-bottom: 10px;
37
+ animation: fadeIn 0.5s ease;
38
+ }
39
+ .user-message {
40
+ background-color: #f0f7ff;
41
+ border-left: 5px solid #3b82f6;
42
+ }
43
+ .assistant-message {
44
+ background-color: #f0fdf4;
45
+ border-left: 5px solid #22c55e;
46
+ }
47
+
48
+ /* Source section styling */
49
+ .source-item {
50
+ padding: 12px;
51
+ border-radius: 8px;
52
+ background-color: #f8fafc;
53
+ border: 1px solid #e2e8f0;
54
+ margin-bottom: 10px;
55
+ transition: all 0.2s ease;
56
+ }
57
+ .source-item:hover {
58
+ border-color: #cbd5e1;
59
+ background-color: #f1f5f9;
60
+ }
61
+ .source-header {
62
+ font-weight: 600;
63
+ display: flex;
64
+ justify-content: space-between;
65
+ margin-bottom: 8px;
66
+ align-items: center;
67
+ }
68
+ .source-content {
69
+ font-size: 0.9em;
70
+ color: #475569;
71
+ max-height: 200px;
72
+ overflow-y: auto;
73
+ }
74
+ .verified-badge {
75
+ background-color: #10b981;
76
+ color: white;
77
+ padding: 2px 8px;
78
+ border-radius: 12px;
79
+ font-size: 0.7em;
80
+ display: inline-flex;
81
+ align-items: center;
82
+ gap: 4px;
83
+ }
84
+
85
+ /* Animated loader */
86
+ @keyframes pulse-animation {
87
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
88
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
89
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
90
+ }
91
+ .pulse {
92
+ animation: pulse-animation 2s infinite;
93
+ }
94
+
95
+ /* Fade in animation */
96
+ @keyframes fadeIn {
97
+ from { opacity: 0; transform: translateY(10px); }
98
+ to { opacity: 1; transform: translateY(0); }
99
+ }
100
+
101
+ /* Method selection buttons */
102
+ .method-button {
103
+ border-radius: 8px;
104
+ padding: 8px 16px;
105
+ transition: all 0.3s ease;
106
+ border: none;
107
+ cursor: pointer;
108
+ font-weight: 500;
109
+ display: inline-flex;
110
+ align-items: center;
111
+ gap: 8px;
112
+ }
113
+ .method-direct {
114
+ background-color: #e0f2fe;
115
+ color: #0284c7;
116
+ }
117
+ .method-direct:hover {
118
+ background-color: #bae6fd;
119
+ }
120
+ .method-enhanced {
121
+ background-color: #dbeafe;
122
+ color: #2563eb;
123
+ }
124
+ .method-enhanced:hover {
125
+ background-color: #bfdbfe;
126
+ }
127
+ .method-active {
128
+ box-shadow: 0 0 0 2px #3b82f6;
129
+ }
130
+
131
+ /* Two-column layout for answer and sources */
132
+ .answer-container {
133
+ border-radius: 12px;
134
+ background-color: white;
135
+ padding: 20px;
136
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
137
+ margin-bottom: 20px;
138
+ }
139
+ .answer-header {
140
+ margin-bottom: 16px;
141
+ color: #1e293b;
142
+ font-weight: 600;
143
+ font-size: 1.1em;
144
+ }
145
+ .answer-content {
146
+ font-size: 1em;
147
+ line-height: 1.6;
148
+ color: #334155;
149
+ }
150
+ .sources-container {
151
+ border-radius: 12px;
152
+ background-color: white;
153
+ padding: 20px;
154
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
155
+ }
156
+ .sources-header {
157
+ margin-bottom: 16px;
158
+ color: #1e293b;
159
+ font-weight: 600;
160
+ font-size: 1.1em;
161
+ }
162
+
163
+ /* Section animations */
164
+ .animate-section {
165
+ animation: fadeIn 0.5s ease;
166
+ }
167
+ </style>
168
+ """, unsafe_allow_html=True)
169
+
170
  # Helper function to initialize session state
171
  def initialize_session_state():
172
  """Initialize Streamlit session state variables."""
 
182
  st.session_state.retrieval_method = "enhanced"
183
  if "voice_transcript" not in st.session_state:
184
  st.session_state.voice_transcript = ""
185
+ if "current_answer" not in st.session_state:
186
+ st.session_state.current_answer = None
187
 
188
  # Helper function to clean up temporary files
189
  def cleanup_temp_files():
 
195
  except Exception as e:
196
  print(f"Error cleaning up temporary directory: {e}")
197
 
198
+ # Create an animated loading spinner
199
+ def animated_loader(text="Processing..."):
200
+ with st.spinner(text):
201
+ # Add a pulsing animation while processing
202
+ st.markdown("""
203
+ <div style="display: flex; justify-content: center; margin: 20px 0;">
204
+ <div class="pulse" style="width: 20px; height: 20px; border-radius: 50%; background-color: #3b82f6;"></div>
205
+ </div>
206
+ """, unsafe_allow_html=True)
207
+
208
+ # Animated section container
209
+ def animated_section(key):
210
+ return st.container(key=f"animated_{key}")
211
+
212
+ # Create a method selection button with animation
213
+ def method_button(label, icon, method, current_method):
214
+ active_class = "method-active" if method == current_method else ""
215
+ method_class = "method-direct" if method == "direct" else "method-enhanced"
216
+
217
+ button_html = f"""
218
+ <button class="method-button {method_class} {active_class}">
219
+ {icon} {label}
220
+ </button>
221
+ """
222
+ return button_html
223
+
224
  # Streamlit UI
225
  def main():
226
  st.set_page_config(
227
+ page_title="Advanced RAG System",
228
  layout="wide",
229
  initial_sidebar_state="expanded"
230
  )
231
 
232
+ # Load custom CSS
233
+ load_custom_css()
234
+
235
+ # Page header with animation
236
+ with animated_section("header"):
237
+ st.title("🚀 Advanced RAG System")
238
+ st.markdown("""
239
+ <div style="display: flex; gap: 15px; margin-bottom: 20px;">
240
+ <div style="background-color: #e0f2fe; color: #0284c7; padding: 8px 16px; border-radius: 20px; font-size: 0.9em; display: flex; align-items: center; gap: 8px;">
241
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
242
+ Document Analysis
243
+ </div>
244
+ <div style="background-color: #f0fdf4; color: #16a34a; padding: 8px 16px; border-radius: 20px; font-size: 0.9em; display: flex; align-items: center; gap: 8px;">
245
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
246
+ Blockchain Verification
247
+ </div>
248
+ <div style="background-color: #fef2f2; color: #dc2626; padding: 8px 16px; border-radius: 20px; font-size: 0.9em; display: flex; align-items: center; gap: 8px;">
249
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>
250
+ Voice Input
251
+ </div>
252
+ </div>
253
+ """, unsafe_allow_html=True)
254
 
255
  # Initialize session state
256
  initialize_session_state()
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  # Sidebar for configuration and file upload
259
  with st.sidebar:
260
+ with animated_section("sidebar_header"):
261
+ st.header("System Configuration")
262
+ st.markdown("""
263
+ <div style="margin-bottom: 15px; padding: 10px; border-radius: 8px; background-color: #f1f5f9; border-left: 4px solid #3b82f6;">
264
+ Configure your RAG system and upload documents
265
+ </div>
266
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
+ # MetaMask Connection
269
+ with animated_section("metamask"):
270
+ st.subheader("🦊 MetaMask Connection")
 
 
 
 
 
 
271
 
272
+ # Add MetaMask connector and get connection info
273
+ metamask_info = metamask_connector()
274
 
275
+ # Display MetaMask connection status
276
  if metamask_info and metamask_info.get("connected"):
277
+ st.success(f"✅ Connected: {metamask_info.get('address')[:10]}...{metamask_info.get('address')[-6:]}")
278
+ st.info(f"Network: {metamask_info.get('network_name')}")
279
+ st.session_state.metamask_connected = True
280
  else:
281
+ st.warning("⚠️ MetaMask not connected")
282
+ st.session_state.metamask_connected = False
283
+
284
+ # Update RAG system with MetaMask connection if needed
285
+ if st.session_state.rag and metamask_info:
286
+ is_connected = st.session_state.rag.update_blockchain_connection(metamask_info)
287
+ if is_connected:
288
+ st.success("RAG system updated with MetaMask connection")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
+ # System Configuration
291
+ with animated_section("config"):
292
+ st.subheader("⚙️ System Configuration")
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
+ # GPU Detection
295
+ gpu_available = torch.cuda.is_available()
296
+ if gpu_available:
297
+ try:
298
+ gpu_info = torch.cuda.get_device_properties(0)
299
+ st.markdown(f"""
300
+ <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px; background-color: #ecfdf5; border-radius: 8px; margin-bottom: 15px;">
301
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 18a5 5 0 0 1-10 0"></path><line x1="12" y1="2" x2="12" y2="9"></line><line x1="4.22" y1="10.22" x2="5.64" y2="11.64"></line><line x1="1" y1="18" x2="3" y2="18"></line><line x1="21" y1="18" x2="23" y2="18"></line><line x1="18.36" y1="11.64" x2="19.78" y2="10.22"></line><line x1="23" y1="22" x2="1" y2="22"></line><polyline points="8 6 12 2 16 6"></polyline></svg>
302
+ <span style="color: #10b981; font-weight: 500;">GPU: {gpu_info.name} ({gpu_info.total_memory / 1024**3:.1f} GB)</span>
303
+ </div>
304
+ """, unsafe_allow_html=True)
305
+ except Exception as e:
306
+ st.warning(f"GPU detected but couldn't get properties")
307
+ else:
308
+ st.warning("No GPU detected. Running in CPU mode.")
 
 
 
 
 
 
 
309
 
310
+ # Model selection
311
+ llm_model = st.selectbox(
312
+ "LLM Model",
313
+ options=[
314
+ "mistralai/Mistral-7B-Instruct-v0.2",
315
+ "google/gemma-7b-it",
316
+ "google/flan-t5-xl",
317
+ "Salesforce/xgen-7b-8k-inst",
318
+ "tiiuae/falcon-7b-instruct"
319
+ ],
320
+ index=0
321
+ )
322
+
323
+ embedding_model = st.selectbox(
324
+ "Embedding Model",
325
+ options=[
326
+ "sentence-transformers/all-mpnet-base-v2",
327
+ "sentence-transformers/all-MiniLM-L6-v2",
328
+ "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
329
+ ],
330
+ index=1
331
+ )
332
+
333
+ use_gpu = st.checkbox("Use GPU Acceleration", value=gpu_available)
334
+
335
+ # Blockchain configuration
336
+ use_blockchain = st.checkbox("Enable Blockchain Verification", value=True)
337
+
338
+ if use_blockchain:
339
+ # Hardcoded contract address - replace with your deployed contract
340
+ contract_address = os.environ.get("CONTRACT_ADDRESS", "0x123abc...") # Your pre-deployed contract
341
+
342
+ st.info(f"Using contract: {contract_address[:10]}...")
343
+
344
+ # Advanced options
345
+ with st.expander("Advanced Options"):
346
+ chunk_size = st.slider("Chunk Size", 100, 2000, 1000)
347
+ chunk_overlap = st.slider("Chunk Overlap", 0, 500, 200)
348
+
349
+ # Initialize button with animation
350
+ if st.button("Initialize System", key="init_button"):
351
+ with st.spinner("Initializing..."):
352
+ animated_loader("Setting up RAG system...")
353
+
354
+ if use_blockchain and not contract_address:
355
+ st.error("Contract address is required for blockchain integration")
356
+ else:
357
+ st.session_state.rag = AdvancedRAG(
358
+ llm_model_name=llm_model,
359
+ embedding_model_name=embedding_model,
360
+ chunk_size=chunk_size,
361
+ chunk_overlap=chunk_overlap,
362
+ use_gpu=use_gpu and gpu_available,
363
+ use_blockchain=use_blockchain,
364
+ contract_address=contract_address if use_blockchain else None
365
+ )
366
 
367
+ # Update with current MetaMask connection if available
368
+ if use_blockchain and metamask_info:
369
+ st.session_state.rag.update_blockchain_connection(metamask_info)
370
 
371
+ st.success(f"System initialized with {embedding_model}")
372
+
373
+ # Document Upload
374
+ with animated_section("upload"):
375
+ st.subheader("📄 Document Upload")
376
+ uploaded_files = st.file_uploader("Select PDFs", type="pdf", accept_multiple_files=True)
377
+
378
+ if uploaded_files and st.button("Process PDFs", key="process_button"):
379
+ if not st.session_state.rag:
380
+ with st.spinner("Initializing system first..."):
381
+ animated_loader("Setting up RAG system...")
382
 
383
+ st.session_state.rag = AdvancedRAG(
384
+ llm_model_name=llm_model,
385
+ embedding_model_name=embedding_model,
386
+ chunk_size=chunk_size,
387
+ chunk_overlap=chunk_overlap,
388
+ use_gpu=use_gpu and gpu_available,
389
+ use_blockchain=use_blockchain,
390
+ contract_address=contract_address if use_blockchain else None
391
+ )
392
 
393
+ # Update with current MetaMask connection if available
394
+ if use_blockchain and metamask_info:
395
+ st.session_state.rag.update_blockchain_connection(metamask_info)
396
+
397
+ with st.spinner("Processing documents..."):
398
+ animated_loader("Analyzing and indexing PDFs...")
399
+
400
+ success = st.session_state.rag.process_pdfs(uploaded_files)
401
+ if success:
402
+ metrics = st.session_state.rag.get_performance_metrics()
403
+ if metrics:
404
+ st.success("📄 PDFs processed successfully!")
405
+ with st.expander("💹 Performance Metrics"):
406
+ st.markdown(f"**Documents processed:** {metrics['documents_processed']} chunks")
407
+ st.markdown(f"**Index building time:** {metrics['index_building_time']:.2f} seconds")
408
+ st.markdown(f"**Total processing time:** {metrics['total_processing_time']:.2f} seconds")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ # Main content area - Two column layout
411
+ main_col1, main_col2 = st.columns([2, 1])
412
 
413
+ # Left column - Chat and Answer section
414
+ with main_col1:
415
+ # Method Selection
416
+ with animated_section("method_selection"):
417
+ st.markdown("### Answer Method")
418
+ col1, col2 = st.columns(2)
419
+
420
+ with col1:
421
+ direct_html = method_button(
422
+ "Direct Retrieval",
423
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
424
+ "direct",
425
+ st.session_state.retrieval_method
426
+ )
427
+ if st.markdown(direct_html, unsafe_allow_html=True):
428
+ st.session_state.retrieval_method = "direct"
429
+ st.rerun()
430
+
431
+ with col2:
432
+ enhanced_html = method_button(
433
+ "Enhanced Answers",
434
+ '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>',
435
+ "enhanced",
436
+ st.session_state.retrieval_method
437
+ )
438
+ if st.markdown(enhanced_html, unsafe_allow_html=True):
439
+ st.session_state.retrieval_method = "enhanced"
440
+ st.rerun()
441
+
442
+ # Show current method description
443
+ if st.session_state.retrieval_method == "direct":
444
+ st.info("🔍 **Direct Retrieval**: Shows raw document passages without processing. Fast and transparent.")
445
+ else:
446
+ st.info("💡 **Enhanced Answers**: Processes content through AI for comprehensive answers. Better quality.")
447
 
448
+ # Voice Input Section
449
+ with animated_section("voice_input"):
450
+ st.markdown("### Ask with Voice")
451
+ voice_transcript = voice_input_component()
452
+
453
+ # Update session state with voice transcript if not empty
454
+ if voice_transcript and voice_transcript.strip():
455
+ st.session_state.voice_transcript = voice_transcript.strip()
456
+ st.experimental_rerun()
457
 
458
+ # Text Input Section
459
+ with animated_section("text_input"):
460
+ st.markdown("### Or Type a Question")
461
+ # Chat input - show the voice transcript in the text input
462
+ user_input = st.text_input(
463
+ "Ask a question about your documents",
464
+ value=st.session_state.voice_transcript,
465
+ key="text_question"
466
+ )
467
+
468
+ # Process user input (from text or voice)
469
+ if user_input or st.session_state.voice_transcript:
470
+ # Prioritize text input over voice input
471
+ if user_input:
472
+ query = user_input
473
+ else:
474
+ query = st.session_state.voice_transcript
475
+ # Clear voice transcript after using it
476
+ st.session_state.voice_transcript = ""
477
+
478
+ # Add user message to chat history
479
+ st.session_state.messages.append({"role": "user", "content": query})
480
+
481
+ # Check if system is initialized
482
+ if not st.session_state.rag:
483
+ st.error("Please initialize the system and process PDFs first.")
484
+ st.session_state.messages.append({
485
+ "role": "assistant",
486
+ "content": "Please initialize the system and process PDFs first."
487
+ })
488
+
489
+ # Get response if vector store is ready
490
+ elif st.session_state.rag.vector_store:
491
+ with st.spinner("Generating answer..."):
492
+ animated_loader("Searching documents and generating answer...")
493
 
494
+ # Get retrieval method
495
+ method = st.session_state.retrieval_method
496
 
497
+ # Get response using specified method
498
+ response = st.session_state.rag.ask(query, method=method)
499
+ st.session_state.messages.append({"role": "assistant", "content": response})
500
 
501
+ # Store current answer for display
502
+ st.session_state.current_answer = response
 
 
503
 
504
+ # Rerun to update the UI
505
+ st.experimental_rerun()
506
+ else:
507
+ st.error("Please upload and process PDF files first.")
508
+ st.session_state.messages.append({
509
+ "role": "assistant",
510
+ "content": "Please upload and process PDF files first."
511
+ })
512
+
513
+ # Answer Display Section
514
+ if st.session_state.current_answer and isinstance(st.session_state.current_answer, dict):
515
+ with animated_section("answer_display"):
516
+ answer = st.session_state.current_answer
517
+
518
+ st.markdown("""
519
+ <div class="answer-container animate-section">
520
+ <div class="answer-header">
521
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
522
+ Answer
523
+ </div>
524
+ <div class="answer-content">
525
+ {answer_text}
526
+ </div>
527
+ </div>
528
+ """.format(answer_text=answer["answer"]), unsafe_allow_html=True)
529
+
530
+ # Display metadata
531
+ meta_cols = st.columns(3)
532
+ with meta_cols[0]:
533
+ method_name = "Direct Retrieval" if answer["method"] == "direct" else "Enhanced Answer"
534
+ st.caption(f"Method: {method_name}")
535
+ with meta_cols[1]:
536
+ st.caption(f"Time: {answer['query_time']:.2f} seconds")
537
+ with meta_cols[2]:
538
+ if "blockchain_log" in answer and answer["blockchain_log"]:
539
+ blockchain_log = answer["blockchain_log"]
540
+ st.caption(f"📝 Logged on blockchain: {blockchain_log['tx_hash'][:8]}...")
541
+
542
+ # Right column - Sources section
543
+ with main_col2:
544
+ if st.session_state.current_answer and isinstance(st.session_state.current_answer, dict):
545
+ with animated_section("sources_display"):
546
+ answer = st.session_state.current_answer
547
+
548
+ st.markdown("""
549
+ <div class="sources-container animate-section">
550
+ <div class="sources-header">
551
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>
552
+ Sources
553
+ </div>
554
+ """, unsafe_allow_html=True)
555
+
556
+ # Display sources
557
+ if "sources" in answer and answer["sources"]:
558
+ for i, source in enumerate(answer["sources"]):
559
+ verified_badge = ""
560
+ if source.get("blockchain"):
561
+ verified_badge = f"""
562
+ <div class="verified-badge">
563
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
564
+ Verified
565
+ </div>
566
+ """
567
+
568
+ st.markdown(f"""
569
+ <div class="source-item">
570
+ <div class="source-header">
571
+ <div>Source {i+1}: {source['source']}</div>
572
+ {verified_badge}
573
+ </div>
574
+ <div class="source-content">
575
+ {source['content']}
576
+ </div>
577
+ </div>
578
+ """, unsafe_allow_html=True)
579
+
580
+ st.markdown("</div>", unsafe_allow_html=True)
581
  else:
582
+ # Placeholder when no sources to display
583
+ st.markdown("""
584
+ <div style="height: 300px; display: flex; justify-content: center; align-items: center; background-color: white; border-radius: 12px; margin-top: 30px;">
585
+ <div style="text-align: center; color: #94a3b8;">
586
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin: 0 auto 15px;"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
587
+ <p>Ask a question to see document sources here</p>
588
+ </div>
589
+ </div>
590
+ """, unsafe_allow_html=True)
591
 
592
  # Main entry point
593
  if __name__ == "__main__":