DavidFernandes commited on
Commit
b17d9d3
Β·
verified Β·
1 Parent(s): 6c4361f

Upload 9 files

Browse files
.streamlit/config.toml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [theme]
2
+ primaryColor = "#3B82F6"
3
+ backgroundColor = "#0F172A"
4
+ secondaryBackgroundColor = "#1E293B"
5
+ textColor = "#E2E8F0"
6
+ font = "sans serif"
7
+
8
+ [server]
9
+ maxUploadSize = 200
10
+ enableXsrfProtection = true
11
+ enableCORS = false
12
+
13
+ [browser]
14
+ gatherUsageStats = false
15
+
16
+ [client]
17
+ toolbarMode = "minimal"
18
+ showSidebarNavigation = true
19
+
20
+ [runner]
21
+ magicEnabled = true
22
+ fastRerenderEnabled = true
23
+
24
+ [logger]
25
+ <<<<<<< HEAD
26
+ level = "info"
27
+ =======
28
+ level = "info"
29
+ >>>>>>> 1bc20a0d3edc7f88f03e506f84b01a7303d403b2
pages/1_πŸ“_Document_Analysis.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+ import datetime
4
+ from theme import apply_dark_theme, show_page_header, show_footer
5
+ from utils import (
6
+ process_image,
7
+ process_pdf,
8
+ initialize_session_state,
9
+ create_chat_engine,
10
+ save_to_history,
11
+ generate_pdf_analysis,
12
+ process_captured_image,
13
+ format_analysis_results
14
+ )
15
+
16
+ # Page config
17
+ st.set_page_config(
18
+ page_title="Document Analysis | CiviDoc AI",
19
+ page_icon="πŸ“",
20
+ layout="centered",
21
+ initial_sidebar_state="collapsed",
22
+ )
23
+
24
+ # Apply dark theme
25
+ st.markdown(apply_dark_theme(), unsafe_allow_html=True)
26
+
27
+ def document_analysis_page():
28
+ # Initialize states
29
+ initialize_session_state()
30
+
31
+ # Header
32
+ st.markdown(show_page_header(
33
+ "πŸ“ Document Analysis",
34
+ "Upload or capture documents for instant analysis"
35
+ ), unsafe_allow_html=True)
36
+
37
+ # Main content tabs - Mobile friendly
38
+ tabs = st.tabs([
39
+ "πŸ“€ Upload ",
40
+ "πŸ“Έ Capture",
41
+ "πŸ“Š Results"
42
+ ])
43
+
44
+ with tabs[0]:
45
+ st.markdown(
46
+ "<div class='card'>"
47
+ "<h3>Upload Documents</h3>"
48
+ "<p style='margin-bottom: 2rem;'>Support for PDF, JPG, JPEG, and PNG files.</p>"
49
+ "<div class='status-badge status-success'>Multiple files supported</div>"
50
+ "</div>",
51
+ unsafe_allow_html=True
52
+ )
53
+
54
+ # Mobile-friendly file uploader
55
+ uploaded_files = st.file_uploader(
56
+ "Drop files or tap to browse",
57
+ type=["jpg", "jpeg", "png", "pdf"],
58
+ accept_multiple_files=True,
59
+ key="doc_uploader"
60
+ )
61
+
62
+ if uploaded_files:
63
+ st.markdown(
64
+ f"<div class='status-badge status-success' style='margin: 1rem 0;'>"
65
+ f"πŸ“Ž {len(uploaded_files)} file(s) uploaded"
66
+ f"</div>",
67
+ unsafe_allow_html=True
68
+ )
69
+
70
+ # File list - Mobile friendly
71
+ st.markdown(
72
+ "<div class='card'>"
73
+ "<h4>Selected Files:</h4>"
74
+ "<div class='touch-spacing'>",
75
+ unsafe_allow_html=True
76
+ )
77
+
78
+ for file in uploaded_files:
79
+ st.markdown(
80
+ f"<div style='display: flex; align-items: center; padding: 0.5rem 0;'>"
81
+ f"<span style='margin-right: 0.5rem;'>πŸ“„</span>{file.name}"
82
+ f"</div>",
83
+ unsafe_allow_html=True
84
+ )
85
+
86
+ st.markdown("</div></div>", unsafe_allow_html=True)
87
+
88
+ # Analysis button - Touch friendly
89
+ if st.button("πŸ” Analyze Documents", use_container_width=True):
90
+ process_uploaded_files(uploaded_files)
91
+
92
+ with tabs[1]:
93
+ st.markdown(
94
+ "<div class='card'>"
95
+ "<h3>Capture Document</h3>"
96
+ "<p>Take a clear photo of your document using your camera.</p>"
97
+ "</div>",
98
+ unsafe_allow_html=True
99
+ )
100
+
101
+ # Mobile-optimized camera input
102
+ picture = st.camera_input(
103
+ "πŸ“Έ Tap to capture",
104
+ help="Make sure the document is well-lit and clearly visible"
105
+ )
106
+
107
+ if picture:
108
+ st.markdown(
109
+ "<div class='status-badge status-success' style='margin: 1rem 0;'>"
110
+ "πŸ“Έ Image captured successfully"
111
+ "</div>",
112
+ unsafe_allow_html=True
113
+ )
114
+
115
+ # Analysis button - Touch friendly
116
+ if st.button("πŸ” Analyze Photo", use_container_width=True):
117
+ process_captured_image(picture)
118
+
119
+ with tabs[2]:
120
+ display_analysis_results()
121
+
122
+ def process_uploaded_files(files):
123
+ """Process multiple uploaded files with mobile-friendly progress tracking"""
124
+ progress_text = "Processing documents..."
125
+ total_files = len(files)
126
+
127
+ # Progress tracking
128
+ progress_placeholder = st.empty()
129
+ progress_bar = st.progress(0)
130
+ status_text = st.empty()
131
+
132
+ for idx, uploaded_file in enumerate(files, 1):
133
+ try:
134
+ # Update progress
135
+ progress_placeholder.markdown(
136
+ f"<div style='text-align: center; margin: 1rem 0;'>"
137
+ f"Processing file {idx} of {total_files}"
138
+ f"</div>",
139
+ unsafe_allow_html=True
140
+ )
141
+
142
+ status_text.markdown(
143
+ f"<div class='status-badge status-warning'>"
144
+ f"πŸ“ Processing: {uploaded_file.name}"
145
+ f"</div>",
146
+ unsafe_allow_html=True
147
+ )
148
+
149
+ if uploaded_file.type in ['image/jpeg', 'image/png']:
150
+ # Process image
151
+ image = Image.open(uploaded_file)
152
+ analysis = process_image(image)
153
+
154
+ # Save results
155
+ st.session_state.analyses[uploaded_file.name] = {
156
+ 'type': uploaded_file.type,
157
+ 'analysis': analysis,
158
+ 'timestamp': datetime.datetime.now()
159
+ }
160
+
161
+ # Create chat engine
162
+ st.session_state.chat_engines[uploaded_file.name] = create_chat_engine(analysis)
163
+
164
+ elif uploaded_file.type == 'application/pdf':
165
+ # Process PDF
166
+ documents = process_pdf(uploaded_file)
167
+ analysis = generate_pdf_analysis(documents)
168
+
169
+ # Save results
170
+ st.session_state.analyses[uploaded_file.name] = {
171
+ 'type': uploaded_file.type,
172
+ 'analysis': analysis,
173
+ 'timestamp': datetime.datetime.now()
174
+ }
175
+
176
+ # Create chat engine
177
+ st.session_state.chat_engines[uploaded_file.name] = create_chat_engine(documents)
178
+
179
+ # Update progress
180
+ progress_bar.progress(idx/total_files)
181
+
182
+ # Save to history
183
+ save_to_history(
184
+ uploaded_file.name,
185
+ uploaded_file.type.split('/')[1].upper(),
186
+ analysis,
187
+ datetime.datetime.now()
188
+ )
189
+
190
+ except Exception as e:
191
+ st.error(
192
+ f"❌ Error processing {uploaded_file.name}\n"
193
+ f"Details: {str(e)}"
194
+ )
195
+
196
+ # Clear progress indicators
197
+ progress_placeholder.empty()
198
+ progress_bar.empty()
199
+
200
+ # Show completion message
201
+ status_text.markdown(
202
+ "<div class='status-badge status-success' style='margin: 1rem 0;'>"
203
+ "βœ… All documents processed successfully!"
204
+ "</div>",
205
+ unsafe_allow_html=True
206
+ )
207
+
208
+ # Mobile-friendly action buttons
209
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
210
+ if st.button("πŸ’¬ Start Chat", use_container_width=True):
211
+ st.switch_page("pages/2_πŸ’¬_Document_Chat.py")
212
+ if st.button("πŸ“€ Process More", use_container_width=True):
213
+ st.rerun()
214
+ st.markdown("</div>", unsafe_allow_html=True)
215
+
216
+ def display_analysis_results():
217
+ """Display analysis results with mobile-friendly layout"""
218
+ if st.session_state.analyses:
219
+ # Mobile-friendly filters
220
+ st.markdown(
221
+ "<div class='card' style='margin-bottom: 1rem;'>"
222
+ "<h4>Filter Results</h4>",
223
+ unsafe_allow_html=True
224
+ )
225
+
226
+ # Touch-friendly filter options
227
+ filter_type = st.multiselect(
228
+ "Document Type",
229
+ ["Image", "PDF", "Captured"],
230
+ default=["Image", "PDF", "Captured"]
231
+ )
232
+
233
+
234
+ st.markdown("</div>", unsafe_allow_html=True)
235
+
236
+ # Display results
237
+ if st.session_state.analyses:
238
+ for filename, data in st.session_state.analyses.items():
239
+ st.markdown(
240
+ f"<div class='card'>"
241
+ f"<div style='display: flex; justify-content: space-between; align-items: center;'>"
242
+ f"<h4>{filename}</h4>"
243
+ f"<div class='status-badge status-success'>{data['type'].split('/')[1].upper()}</div>"
244
+ f"</div>"
245
+ f"<hr style='margin: 0.5rem 0;'>"
246
+ f"{format_analysis_results(data['analysis'])}" # Use the formatting function
247
+ f"<div class='touch-spacing'>"
248
+ f"</div>"
249
+ f"</div>",
250
+ unsafe_allow_html=True
251
+ )
252
+ else:
253
+ st.markdown(
254
+ "<div class='card' style='text-align: center;'>"
255
+ "<h3>No Documents Analyzed</h3>"
256
+ "<p>Upload or capture documents to see analysis results here.</p>"
257
+ "</div>",
258
+ unsafe_allow_html=True
259
+ )
260
+
261
+
262
+ if __name__ == "__main__":
263
+ document_analysis_page()
pages/2_πŸ’¬_Document_Chat.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from theme import apply_dark_theme, show_page_header, show_footer
3
+ from utils import initialize_session_state
4
+
5
+ # Page config
6
+ st.set_page_config(
7
+ page_title="Document Chat | CiviDoc AI",
8
+ page_icon="πŸ’¬",
9
+ layout="centered",
10
+ initial_sidebar_state="collapsed",
11
+
12
+ )
13
+
14
+ # Apply dark theme
15
+ st.markdown(apply_dark_theme(), unsafe_allow_html=True)
16
+
17
+ def document_chat_page():
18
+ # Initialize states
19
+ initialize_session_state()
20
+
21
+ # Header
22
+ st.markdown(show_page_header(
23
+ "πŸ’¬ Document Chat",
24
+ "Get instant answers about your documents"
25
+ ), unsafe_allow_html=True)
26
+
27
+ if st.session_state.chat_engines:
28
+ # Document selector - Mobile friendly
29
+ st.markdown(
30
+ "<div class='card'>"
31
+ "<h4>Select Document</h4>",
32
+ unsafe_allow_html=True
33
+ )
34
+
35
+ doc_names = list(st.session_state.chat_engines.keys())
36
+ selected_doc = st.selectbox(
37
+ "Choose a document to discuss:",
38
+ doc_names,
39
+ key="doc_selector",
40
+ format_func=lambda x: f"πŸ“„ {x}"
41
+ )
42
+
43
+ st.markdown("</div>", unsafe_allow_html=True)
44
+
45
+ if selected_doc:
46
+ # Document preview
47
+ with st.expander("πŸ“„ Document Content", expanded=False):
48
+ if selected_doc in st.session_state.analyses:
49
+ st.markdown(
50
+ f"<div class='card'>{st.session_state.analyses[selected_doc]['analysis']}</div>",
51
+ unsafe_allow_html=True
52
+ )
53
+
54
+ # Initialize chat history for selected document
55
+ if 'messages' not in st.session_state:
56
+ st.session_state.messages = [
57
+ {"role": "assistant", "content": f"Hello! I'm here to help you understand {selected_doc}. What would you like to know?"}
58
+ ]
59
+
60
+ # Display chat messages
61
+ for message in st.session_state.messages:
62
+ with st.chat_message(message["role"]):
63
+ st.write(message["content"])
64
+
65
+ # Suggested questions
66
+ if len(st.session_state.messages) <= 1:
67
+ st.markdown(
68
+ "<div class='card'>"
69
+ "<h4>Suggested Questions</h4>"
70
+ "<div class='touch-spacing'>",
71
+ unsafe_allow_html=True
72
+ )
73
+
74
+ suggestions = [
75
+ "What is this document about?",
76
+ "What are the main requirements?",
77
+ "Are there any deadlines I should know about?",
78
+ "Can you explain the technical terms?",
79
+ "What actions do I need to take?"
80
+ ]
81
+
82
+ for suggestion in suggestions:
83
+ if st.button(f"πŸ” {suggestion}", use_container_width=True):
84
+ handle_user_input(suggestion, selected_doc)
85
+
86
+ st.markdown("</div></div>", unsafe_allow_html=True)
87
+
88
+ # Chat input
89
+ if prompt := st.chat_input("Type your question here..."):
90
+ handle_user_input(prompt, selected_doc)
91
+
92
+ # Clear chat button
93
+ if len(st.session_state.messages) > 1:
94
+ if st.button("πŸ—‘οΈ Clear Chat", use_container_width=True):
95
+ st.session_state.messages = [st.session_state.messages[0]]
96
+ st.rerun()
97
+
98
+ else:
99
+ # No documents message
100
+ st.markdown(
101
+ "<div class='card' style='text-align: center;'>"
102
+ "<h3>No Documents Available</h3>"
103
+ "<p>Please analyze some documents first to start chatting.</p>"
104
+ "<div style='margin-top: 1rem;'>",
105
+ unsafe_allow_html=True
106
+ )
107
+
108
+ if st.button("πŸ“ Go to Document Analysis", use_container_width=True):
109
+ st.switch_page("pages/1_πŸ“_Document_Analysis.py")
110
+
111
+ st.markdown("</div></div>", unsafe_allow_html=True)
112
+
113
+ def handle_user_input(prompt, selected_doc):
114
+ """Handle user input with loading states and error handling"""
115
+ # Add user message
116
+ st.session_state.messages.append({"role": "user", "content": prompt})
117
+
118
+ # Display user message
119
+ with st.chat_message("user"):
120
+ st.write(prompt)
121
+
122
+ # Generate and display assistant response
123
+ with st.chat_message("assistant"):
124
+ with st.spinner("Thinking..."):
125
+ try:
126
+ chat_engine = st.session_state.chat_engines[selected_doc]
127
+ response = chat_engine.chat(prompt)
128
+ assistant_response = response.response
129
+
130
+ # Add assistant response to messages
131
+ st.session_state.messages.append({
132
+ "role": "assistant",
133
+ "content": assistant_response
134
+ })
135
+
136
+ # Display response
137
+ st.write(assistant_response)
138
+
139
+ # Show related questions
140
+ with st.expander("Related Questions", expanded=False):
141
+ suggestions = generate_related_questions(prompt, assistant_response)
142
+ for suggestion in suggestions:
143
+ if st.button(f"πŸ” {suggestion}", key=f"related_{suggestion}", use_container_width=True):
144
+ handle_user_input(suggestion, selected_doc)
145
+
146
+ except Exception as e:
147
+ error_message = f"Sorry, I encountered an error: {str(e)}"
148
+ st.error(error_message)
149
+ st.session_state.messages.append({
150
+ "role": "assistant",
151
+ "content": error_message
152
+ })
153
+
154
+ def generate_related_questions(prompt, response):
155
+ """Generate related questions based on the conversation context"""
156
+ # Add your logic to generate related questions
157
+ return [
158
+ "Can you explain this in simpler terms?",
159
+ "What are the next steps?",
160
+ "Are there any requirements I should know about?",
161
+ "Where can I find more information?"
162
+ ]
163
+
164
+ if __name__ == "__main__":
165
+ document_chat_page()
pages/3_✍️_Writing_Assistant.py ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from theme import apply_dark_theme, show_page_header, show_footer
3
+ from utils import initialize_session_state, generate_document, save_to_history
4
+ from datetime import datetime
5
+
6
+ # Page config
7
+ st.set_page_config(
8
+ page_title="Writing Assistant | CiviDoc AI",
9
+ page_icon="✍️",
10
+ layout="centered",
11
+ initial_sidebar_state="collapsed",
12
+ )
13
+
14
+ # Apply dark theme
15
+ st.markdown(apply_dark_theme(), unsafe_allow_html=True)
16
+
17
+ def writing_assistant_page():
18
+ # Initialize states
19
+ initialize_session_state()
20
+
21
+ # Header
22
+ st.markdown(show_page_header(
23
+ "✍️ Writing Assistant",
24
+ "Create professional government documents effortlessly"
25
+ ), unsafe_allow_html=True)
26
+
27
+ # Document Type Selection
28
+ st.markdown(
29
+ "<div class='card'>"
30
+ "<h3>Select Document Type</h3>"
31
+ "<p>Choose the type of document you need to create</p>"
32
+ "</div>",
33
+ unsafe_allow_html=True
34
+ )
35
+
36
+ doc_types = {
37
+ "RTI": "πŸ“ RTI Application",
38
+ "COMPLAINT": "πŸ“’ Complaint Letter",
39
+ "LEGAL": "βš–οΈ Legal Notice",
40
+ "APPEAL": "πŸ“¨ Appeal Letter",
41
+ "PERMISSION": "πŸ”‘ Permission Request",
42
+ "APPLICATION": "πŸ“‹ Government Application",
43
+ "CUSTOM": "βœ’οΈ Custom Document"
44
+ }
45
+
46
+ # Mobile-friendly document type selector with icons
47
+ selected_type = st.selectbox(
48
+ "Document Type",
49
+ options=list(doc_types.keys()),
50
+ format_func=lambda x: doc_types[x],
51
+ key="doc_type_selector"
52
+ )
53
+
54
+ # Common Fields Section - Always visible
55
+ st.markdown(
56
+ "<div class='card'>"
57
+ "<h4>Personal Information</h4>",
58
+ unsafe_allow_html=True
59
+ )
60
+
61
+ name = st.text_input("Full Name", placeholder="Enter your full name")
62
+ address = st.text_area("Address", placeholder="Enter your complete address")
63
+ contact = st.text_input("Contact Number", placeholder="Enter your contact number")
64
+ email = st.text_input("Email Address", placeholder="Enter your email address")
65
+
66
+ st.markdown("</div>", unsafe_allow_html=True)
67
+
68
+ # Dynamic Form Based on Selection
69
+ if selected_type == "RTI":
70
+ show_rti_form(name, address, contact, email)
71
+ elif selected_type == "COMPLAINT":
72
+ show_complaint_form(name, address, contact, email)
73
+ elif selected_type == "LEGAL":
74
+ show_legal_notice_form(name, address, contact, email)
75
+ elif selected_type == "APPEAL":
76
+ show_appeal_form(name, address, contact, email)
77
+ elif selected_type == "PERMISSION":
78
+ show_permission_form(name, address, contact, email)
79
+ elif selected_type == "APPLICATION":
80
+ show_application_form(name, address, contact, email)
81
+ else: # CUSTOM
82
+ show_custom_form(name, address, contact, email)
83
+
84
+ def show_rti_form(name, address, contact, email):
85
+ """RTI Application Form"""
86
+ st.markdown(
87
+ "<div class='card'>"
88
+ "<h4>RTI Application Details</h4>",
89
+ unsafe_allow_html=True
90
+ )
91
+
92
+ department = st.text_input("Department/Authority Name", placeholder="Enter department name")
93
+ subject = st.text_input("Subject of Information", placeholder="Enter subject")
94
+
95
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
96
+ information = st.text_area(
97
+ "Information Required",
98
+ placeholder="Clearly specify the information you are seeking...",
99
+ height=150
100
+ )
101
+
102
+ time_period = st.text_input(
103
+ "Time Period",
104
+ placeholder="Specify the time period for which information is sought"
105
+ )
106
+
107
+ st.markdown("</div></div>", unsafe_allow_html=True)
108
+
109
+ if validate_and_generate("RTI Application", locals()):
110
+ st.balloons()
111
+
112
+ def show_complaint_form(name, address, contact, email):
113
+ """Complaint Letter Form"""
114
+ st.markdown(
115
+ "<div class='card'>"
116
+ "<h4>Complaint Details</h4>",
117
+ unsafe_allow_html=True
118
+ )
119
+
120
+ authority = st.text_input("Authority/Department Name", placeholder="Enter authority name")
121
+
122
+ complaint_types = [
123
+ "Public Service",
124
+ "Infrastructure",
125
+ "Government Employee",
126
+ "Civic Issue",
127
+ "Other"
128
+ ]
129
+
130
+ complaint_type = st.selectbox("Type of Complaint", complaint_types)
131
+
132
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
133
+ description = st.text_area(
134
+ "Complaint Description",
135
+ placeholder="Describe your complaint in detail...",
136
+ height=150
137
+ )
138
+
139
+ previous_complaints = st.text_area(
140
+ "Previous Complaints (if any)",
141
+ placeholder="Mention any previous complaints filed regarding this issue..."
142
+ )
143
+
144
+ st.markdown("</div></div>", unsafe_allow_html=True)
145
+
146
+ if validate_and_generate("Complaint Letter", locals()):
147
+ st.balloons()
148
+
149
+ def show_legal_notice_form(name, address, contact, email):
150
+ """Legal Notice Form"""
151
+ st.markdown(
152
+ "<div class='card'>"
153
+ "<h4>Legal Notice Details</h4>",
154
+ unsafe_allow_html=True
155
+ )
156
+
157
+ recipient = st.text_input("Notice To (Name/Department)", placeholder="Enter recipient's name")
158
+ recipient_address = st.text_area("Recipient's Address", placeholder="Enter recipient's address")
159
+
160
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
161
+ subject = st.text_input("Subject of Notice", placeholder="Enter notice subject")
162
+ cause = st.text_area(
163
+ "Cause of Action",
164
+ placeholder="Describe the reason for this legal notice...",
165
+ height=100
166
+ )
167
+
168
+ relief_sought = st.text_area(
169
+ "Relief Sought",
170
+ placeholder="Specify what action you want taken...",
171
+ height=100
172
+ )
173
+
174
+ time_period = st.number_input(
175
+ "Response Time Period (in days)",
176
+ min_value=1,
177
+ value=15
178
+ )
179
+
180
+ st.markdown("</div></div>", unsafe_allow_html=True)
181
+
182
+ if validate_and_generate("Legal Notice", locals()):
183
+ st.balloons()
184
+
185
+ def show_appeal_form(name, address, contact, email):
186
+ """Appeal Letter Form"""
187
+ st.markdown(
188
+ "<div class='card'>"
189
+ "<h4>Appeal Details</h4>",
190
+ unsafe_allow_html=True
191
+ )
192
+
193
+ authority = st.text_input("Appellate Authority", placeholder="Enter authority name")
194
+ reference = st.text_input("Previous Reference/Order Number", placeholder="Enter reference number")
195
+
196
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
197
+ order_date = st.date_input("Date of Previous Order")
198
+
199
+ grounds = st.text_area(
200
+ "Grounds for Appeal",
201
+ placeholder="Explain the reasons for your appeal...",
202
+ height=150
203
+ )
204
+
205
+ relief = st.text_area(
206
+ "Relief Sought",
207
+ placeholder="Specify what you are seeking through this appeal...",
208
+ height=100
209
+ )
210
+
211
+ st.markdown("</div></div>", unsafe_allow_html=True)
212
+
213
+ if validate_and_generate("Appeal Letter", locals()):
214
+ st.balloons()
215
+
216
+ def show_permission_form(name, address, contact, email):
217
+ """Permission Request Form"""
218
+ st.markdown(
219
+ "<div class='card'>"
220
+ "<h4>Permission Request Details</h4>",
221
+ unsafe_allow_html=True
222
+ )
223
+
224
+ authority = st.text_input("Authority Name", placeholder="Enter authority name")
225
+ purpose = st.text_input("Purpose of Request", placeholder="Enter the purpose")
226
+
227
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
228
+ details = st.text_area(
229
+ "Request Details",
230
+ placeholder="Provide detailed information about your request...",
231
+ height=150
232
+ )
233
+
234
+ duration = st.text_input("Duration (if applicable)", placeholder="Specify time period")
235
+ location = st.text_input("Location (if applicable)", placeholder="Specify location")
236
+
237
+ undertaking = st.text_area(
238
+ "Undertaking/Declaration",
239
+ placeholder="Any declarations or undertakings...",
240
+ height=100
241
+ )
242
+
243
+ st.markdown("</div></div>", unsafe_allow_html=True)
244
+
245
+ if validate_and_generate("Permission Request", locals()):
246
+ st.balloons()
247
+
248
+ def show_application_form(name, address, contact, email):
249
+ """Government Application Form"""
250
+ st.markdown(
251
+ "<div class='card'>"
252
+ "<h4>Application Details</h4>",
253
+ unsafe_allow_html=True
254
+ )
255
+
256
+ department = st.text_input("Department Name", placeholder="Enter department name")
257
+ purpose = st.text_input("Purpose of Application", placeholder="Enter purpose")
258
+
259
+ application_types = [
260
+ "License",
261
+ "Certificate",
262
+ "Registration",
263
+ "Permit",
264
+ "Other"
265
+ ]
266
+
267
+ app_type = st.selectbox("Application Type", application_types)
268
+
269
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
270
+ details = st.text_area(
271
+ "Application Details",
272
+ placeholder="Provide detailed information...",
273
+ height=150
274
+ )
275
+
276
+ supporting_docs = st.text_area(
277
+ "Supporting Documents",
278
+ placeholder="List all supporting documents...",
279
+ height=100
280
+ )
281
+
282
+ st.markdown("</div></div>", unsafe_allow_html=True)
283
+
284
+ if validate_and_generate("Government Application", locals()):
285
+ st.balloons()
286
+
287
+ def show_custom_form(name, address, contact, email):
288
+ """Custom Document Form"""
289
+ st.markdown(
290
+ "<div class='card'>"
291
+ "<h4>Custom Document Details</h4>",
292
+ unsafe_allow_html=True
293
+ )
294
+
295
+ title = st.text_input("Document Title", placeholder="Enter document title")
296
+ recipient = st.text_input("Recipient/Authority", placeholder="Enter recipient name")
297
+
298
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
299
+ subject = st.text_input("Subject", placeholder="Enter subject")
300
+
301
+ content = st.text_area(
302
+ "Document Content",
303
+ placeholder="Enter the main content of your document...",
304
+ height=300
305
+ )
306
+
307
+ st.markdown("</div></div>", unsafe_allow_html=True)
308
+
309
+ if validate_and_generate("Custom Document", locals()):
310
+ st.balloons()
311
+
312
+ def validate_and_generate(doc_type, fields):
313
+ """Validate fields and generate document"""
314
+ if st.button(f"Generate {doc_type}", use_container_width=True):
315
+ # Basic validation
316
+ required_fields = {k: v for k, v in fields.items()
317
+ if k not in ['st', 'contact', 'email']}
318
+
319
+ empty_fields = [k for k, v in required_fields.items()
320
+ if not v or (isinstance(v, str) and not v.strip())]
321
+
322
+ if empty_fields:
323
+ st.error(
324
+ f"Please fill in all required fields: "
325
+ f"{', '.join(empty_fields)}"
326
+ )
327
+ return False
328
+
329
+ try:
330
+ with st.spinner("Generating document..."):
331
+ # Generate document
332
+ generated_content = generate_document(doc_type, fields)
333
+
334
+ # Save to history
335
+ timestamp = datetime.now()
336
+ doc_name = f"{doc_type}_{timestamp.strftime('%Y%m%d_%H%M%S')}"
337
+ save_to_history(doc_name, doc_type, generated_content, timestamp)
338
+
339
+ # Show success message
340
+ st.success("Document generated successfully!")
341
+
342
+ # Display generated document
343
+ st.markdown(
344
+ "<div class='card'>"
345
+ "<h4>Generated Document</h4>"
346
+ f"<pre style='white-space: pre-wrap;'>{generated_content}</pre>"
347
+ "</div>",
348
+ unsafe_allow_html=True
349
+ )
350
+
351
+ # Action buttons
352
+ col1, col2 = st.columns(2)
353
+ with col1:
354
+ st.download_button(
355
+ "πŸ“₯ Download Document",
356
+ generated_content,
357
+ file_name=f"{doc_name}.txt",
358
+ mime="text/plain",
359
+ use_container_width=True
360
+ )
361
+
362
+ with col2:
363
+ if st.button("πŸ“‹ Create Another", use_container_width=True):
364
+ st.rerun()
365
+
366
+ return True
367
+
368
+ except Exception as e:
369
+ st.error(f"Error generating document: {str(e)}")
370
+ return False
371
+
372
+ return False
373
+
374
+ if __name__ == "__main__":
375
+ writing_assistant_page()
pages/4_πŸ“š_History.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ from datetime import datetime
4
+ from utils import initialize_session_state, get_document_history, delete_from_history, format_timestamp, format_analysis_results
5
+
6
+ def display_document_content(content):
7
+ """Display formatted document content"""
8
+ st.markdown(
9
+ f"<div class='card'>"
10
+ f"<h4>Document Analysis</h4>"
11
+ f"{format_analysis_results(content)}"
12
+ f"</div>",
13
+ unsafe_allow_html=True
14
+ )
15
+ def document_history_page():
16
+ st.title("πŸ“š Document History")
17
+ initialize_session_state()
18
+
19
+ # Create DataFrame from document history
20
+ if st.session_state.document_history:
21
+ history_data = []
22
+ for doc_name, details in get_document_history().items():
23
+ history_data.append({
24
+ 'Document Name': doc_name,
25
+ 'Type': details['type'],
26
+ 'Date': format_timestamp(details['timestamp']),
27
+ 'Status': details['status']
28
+ })
29
+
30
+ df = pd.DataFrame(history_data)
31
+
32
+ # Filters
33
+ col1, col2 = st.columns(2)
34
+ with col1:
35
+ doc_type_filter = st.multiselect(
36
+ "Filter by Document Type",
37
+ options=df['Type'].unique(),
38
+ default=[]
39
+ )
40
+
41
+ with col2:
42
+ date_range = st.date_input(
43
+ "Filter by Date Range",
44
+ value=(datetime.now().date(), datetime.now().date()),
45
+ key="date_range"
46
+ )
47
+
48
+ # Apply filters
49
+ filtered_df = df.copy()
50
+ if doc_type_filter:
51
+ filtered_df = filtered_df[filtered_df['Type'].isin(doc_type_filter)]
52
+ if len(date_range) == 2:
53
+ filtered_df['Date'] = pd.to_datetime(filtered_df['Date'])
54
+ mask = (filtered_df['Date'].dt.date >= date_range[0]) & (filtered_df['Date'].dt.date <= date_range[1])
55
+ filtered_df = filtered_df[mask]
56
+
57
+ # Display interactive table
58
+ st.dataframe(
59
+ filtered_df,
60
+ column_config={
61
+ "Document Name": st.column_config.TextColumn(
62
+ "Document Name",
63
+ width="medium",
64
+ ),
65
+ "Type": st.column_config.TextColumn(
66
+ "Type",
67
+ width="small",
68
+ ),
69
+ "Date": st.column_config.TextColumn(
70
+ "Processing Date",
71
+ width="small",
72
+ ),
73
+ "Status": st.column_config.TextColumn(
74
+ "Status",
75
+ width="small",
76
+ ),
77
+ },
78
+ hide_index=True,
79
+ )
80
+
81
+ # Document Details Section
82
+ st.subheader("Document Details")
83
+ selected_doc = st.selectbox(
84
+ "Select a document to view details",
85
+ options=df['Document Name'].tolist()
86
+ )
87
+
88
+ if selected_doc:
89
+ doc_details = st.session_state.document_history[selected_doc]
90
+
91
+ col1, col2, col3 = st.columns([2,2,1])
92
+ with col1:
93
+ st.markdown(f"**Type:** {doc_details['type']}")
94
+ st.markdown(f"**Processed on:** {format_timestamp(doc_details['timestamp'])}")
95
+
96
+ with col2:
97
+ st.markdown(f"**Status:** {doc_details['status']}")
98
+
99
+ with col3:
100
+ if st.button("Delete Document", key=f"delete_{selected_doc}"):
101
+ delete_from_history(selected_doc)
102
+ st.rerun()
103
+
104
+ # Display document content
105
+ display_document_content(doc_details['content'])
106
+ #with st.expander("Document Content", expanded=True):
107
+ #st.markdown(doc_details['content'])
108
+
109
+ # Actions
110
+ st.subheader("Actions")
111
+ col1, col2, col3 = st.columns(3)
112
+
113
+ with col1:
114
+ if st.button("Chat about Document", use_container_width=True):
115
+ st.session_state.current_doc = selected_doc
116
+ st.switch_page("pages/2_πŸ’¬_Document_Chat.py")
117
+
118
+ with col2:
119
+ if st.button("Download Document", use_container_width=True):
120
+ # Create downloadable version
121
+ content = doc_details['content']
122
+ st.download_button(
123
+ label="Download",
124
+ data=content.encode(),
125
+ file_name=f"{selected_doc}.txt",
126
+ mime="text/plain"
127
+ )
128
+
129
+ with col3:
130
+ if st.button("Share Document", use_container_width=True):
131
+ # Generate shareable link or copy to clipboard
132
+ st.info("Document sharing functionality coming soon!")
133
+
134
+ else:
135
+ st.info("No documents in history. Start by analyzing or creating documents!")
136
+
137
+ col1, col2 = st.columns(2)
138
+ with col1:
139
+ if st.button("Analyze Documents", use_container_width=True):
140
+ st.switch_page("pages/1_πŸ“_Document_Analysis.py")
141
+
142
+ with col2:
143
+ if st.button("Create New Document", use_container_width=True):
144
+ st.switch_page("pages/3_✍️_Writing_Assistant.py")
145
+
146
+ if __name__ == "__main__":
147
+ document_history_page()
requirements.txt ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <<<<<<< HEAD
2
+ groq
3
+ python-dotenv
4
+ langchain
5
+ langchain-community
6
+ sentence-transformers
7
+ transformers
8
+ llama-cloud==0.1.4
9
+ llama-index==0.11.22
10
+ llama-index-agent-openai==0.3.4
11
+ llama-index-cli==0.3.1
12
+ llama-index-core==0.11.22
13
+ llama-index-embeddings-langchain==0.2.1
14
+ llama-index-embeddings-openai==0.2.5
15
+ llama-index-indices-managed-llama-cloud==0.4.0
16
+ llama-index-legacy==0.9.48.post4
17
+ llama-index-llms-groq==0.2.0
18
+ llama-index-llms-openai==0.2.16
19
+ llama-index-llms-openai-like==0.2.0
20
+ llama-index-multi-modal-llms-openai==0.2.3
21
+ llama-index-program-openai==0.2.0
22
+ llama-index-question-gen-openai==0.2.0
23
+ llama-index-readers-file==0.2.2
24
+ llama-index-readers-llama-parse==0.3.0
25
+ llama-parse==0.5.13
26
+ =======
27
+ groq
28
+ python-dotenv
29
+ langchain
30
+ langchain-community
31
+ sentence-transformers
32
+ transformers
33
+ llama-cloud==0.1.4
34
+ llama-index==0.11.22
35
+ llama-index-agent-openai==0.3.4
36
+ llama-index-cli==0.3.1
37
+ llama-index-core==0.11.22
38
+ llama-index-embeddings-langchain==0.2.1
39
+ llama-index-embeddings-openai==0.2.5
40
+ llama-index-indices-managed-llama-cloud==0.4.0
41
+ llama-index-legacy==0.9.48.post4
42
+ llama-index-llms-groq==0.2.0
43
+ llama-index-llms-openai==0.2.16
44
+ llama-index-llms-openai-like==0.2.0
45
+ llama-index-multi-modal-llms-openai==0.2.3
46
+ llama-index-program-openai==0.2.0
47
+ llama-index-question-gen-openai==0.2.0
48
+ llama-index-readers-file==0.2.2
49
+ llama-index-readers-llama-parse==0.3.0
50
+ llama-parse==0.5.13
51
+ >>>>>>> 1bc20a0d3edc7f88f03e506f84b01a7303d403b2
theme.py ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <<<<<<< HEAD
2
+
3
+ def apply_dark_theme():
4
+ return (
5
+ "<style>"
6
+ # Base styles
7
+ "body { background-color: #0F172A; color: #E2E8F0; }"
8
+ ".main { padding: 0rem 1rem; }"
9
+
10
+ # Button styles
11
+ ".stButton>button {"
12
+ " width: 100%;"
13
+ " padding: 1rem;"
14
+ " font-size: 1.1em;"
15
+ " border-radius: 10px;"
16
+ " background-color: #1E293B;"
17
+ " color: #E2E8F0;"
18
+ " border: 1px solid #3B82F6;"
19
+ " box-shadow: 0 2px 5px rgba(0,0,0,0.3);"
20
+ " transition: all 0.2s ease;"
21
+ "}"
22
+ ".stButton>button:hover {"
23
+ " transform: translateY(-2px);"
24
+ " box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);"
25
+ " border-color: #60A5FA;"
26
+ " background-color: #2D3748;"
27
+ "}"
28
+
29
+ # Card styles
30
+ ".card {"
31
+ " padding: 1.5rem;"
32
+ " border-radius: 10px;"
33
+ " background-color: #1E293B;"
34
+ " margin-bottom: 1rem;"
35
+ " border: 1px solid #2D3748;"
36
+ " box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);"
37
+ " transition: all 0.2s ease;"
38
+ "}"
39
+ ".card:hover {"
40
+ " border-color: #3B82F6;"
41
+ " box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);"
42
+ " transform: translateY(-2px);"
43
+ "}"
44
+
45
+ # Typography
46
+ "h1, h2, h3, h4, h5, h6 { color: #60A5FA; }"
47
+ "h1 { font-size: 2.5em; font-weight: 700; }"
48
+ "h2 { font-size: 2em; font-weight: 600; }"
49
+ "h3 { font-size: 1.5em; font-weight: 600; }"
50
+ "p { line-height: 1.6; }"
51
+
52
+ # Form elements
53
+ ".stTextInput>div>div>input,"
54
+ ".stTextArea>div>div>textarea,"
55
+ ".stSelectbox>div>div>select {"
56
+ " background-color: #1E293B;"
57
+ " color: #E2E8F0;"
58
+ " border: 1px solid #3B82F6;"
59
+ " border-radius: 8px;"
60
+ "}"
61
+
62
+ # File uploader
63
+ ".stFileUploader>div>button {"
64
+ " background-color: #1E293B;"
65
+ " color: #E2E8F0;"
66
+ " border: 1px dashed #3B82F6;"
67
+ " border-radius: 8px;"
68
+ "}"
69
+ ".stFileUploader>div>button:hover {"
70
+ " border-color: #60A5FA;"
71
+ " background-color: #2D3748;"
72
+ "}"
73
+
74
+ # Expander
75
+ ".streamlit-expanderHeader {"
76
+ " background-color: #1E293B;"
77
+ " color: #E2E8F0;"
78
+ " border-radius: 8px;"
79
+ "}"
80
+ ".streamlit-expanderContent {"
81
+ " background-color: #1E293B;"
82
+ " border: 1px solid #2D3748;"
83
+ " border-radius: 0 0 8px 8px;"
84
+ "}"
85
+
86
+ # Tabs
87
+ ".stTabs [data-baseweb='tab-list'] {"
88
+ " gap: 30px;"
89
+ " background-color: #1E293B;"
90
+ " padding: 1.5rem;"
91
+ " border-radius: 8px;"
92
+ "}"
93
+ ".stTabs [data-baseweb='tab'] {"
94
+ " height: 40px;"
95
+ " padding: 1.5rem;"
96
+ " background-color: #2D3748;"
97
+ " border-radius: 8px;"
98
+ " color: #E2E8F0;"
99
+ " border: 1px solid #3B82F6;"
100
+ "}"
101
+ ".stTabs [aria-selected='true'] {"
102
+ " background-color: #3B82F6;"
103
+ "}"
104
+
105
+ # Chat elements
106
+ ".chat-message {"
107
+ " padding: 1rem;"
108
+ " margin: 0.5rem 0;"
109
+ " border-radius: 8px;"
110
+ " background-color: #1E293B;"
111
+ " border: 1px solid #2D3748;"
112
+ "}"
113
+ ".user-message {"
114
+ " background-color: #2D3748;"
115
+ " border-color: #3B82F6;"
116
+ "}"
117
+
118
+ # Status indicators
119
+ ".status-badge {"
120
+ " display: inline-block;"
121
+ " padding: 0.25rem 0.75rem;"
122
+ " border-radius: 9999px;"
123
+ " font-size: 0.875rem;"
124
+ " font-weight: 500;"
125
+ " text-align: center;"
126
+ "}"
127
+ ".status-success { background-color: #065F46; color: #6EE7B7; }"
128
+ ".status-warning { background-color: #92400E; color: #FCD34D; }"
129
+ ".status-error { background-color: #991B1B; color: #FCA5A5; }"
130
+
131
+ # Tooltips
132
+ ".tooltip {"
133
+ " position: relative;"
134
+ " display: inline-block;"
135
+ "}"
136
+ ".tooltip .tooltiptext {"
137
+ " visibility: hidden;"
138
+ " background-color: #2D3748;"
139
+ " color: #E2E8F0;"
140
+ " text-align: center;"
141
+ " padding: 0.5rem;"
142
+ " border-radius: 6px;"
143
+ " border: 1px solid #3B82F6;"
144
+ " position: absolute;"
145
+ " z-index: 1;"
146
+ " bottom: 125%;"
147
+ " left: 50%;"
148
+ " transform: translateX(-50%);"
149
+ " opacity: 0;"
150
+ " transition: opacity 0.2s;"
151
+ "}"
152
+ ".tooltip:hover .tooltiptext {"
153
+ " visibility: visible;"
154
+ " opacity: 1;"
155
+ "}"
156
+
157
+ # Progress indicators
158
+ ".progress-bar {"
159
+ " width: 100%;"
160
+ " height: 8px;"
161
+ " background-color: #2D3748;"
162
+ " border-radius: 4px;"
163
+ " overflow: hidden;"
164
+ "}"
165
+ ".progress-bar-fill {"
166
+ " height: 100%;"
167
+ " background-color: #3B82F6;"
168
+ " transition: width 0.3s ease;"
169
+ "}"
170
+
171
+ # Custom scrollbar
172
+ "::-webkit-scrollbar { width: 8px; height: 8px; }"
173
+ "::-webkit-scrollbar-track { background: #1E293B; }"
174
+ "::-webkit-scrollbar-thumb { background: #3B82F6; border-radius: 4px; }"
175
+ "::-webkit-scrollbar-thumb:hover { background: #60A5FA; }"
176
+
177
+ # Animations
178
+ "@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }"
179
+ ".fade-in { animation: fadeIn 0.3s ease-in; }"
180
+
181
+ # Footer
182
+ ".footer {"
183
+ " text-align: center;"
184
+ " color: #94A3B8;"
185
+ " padding: 2rem 1rem;"
186
+ " margin-top: 2rem;"
187
+ " border-top: 1px solid #2D3748;"
188
+ "}"
189
+ "</style>"
190
+ )
191
+
192
+ def show_page_header(title, description=None):
193
+ header_html = (
194
+ "<div style='background-color: #1E293B; padding: 2rem; "+
195
+ "border-radius: 10px; margin-bottom: 2rem;'>"+
196
+ "<h1 style='color: #60A5FA; margin-bottom: 0.5rem;'>" + title + "</h1>"
197
+ )
198
+ if description:
199
+ header_html += (
200
+ "<p style='color: #E2E8F0; font-size: 1.1em;'>" + description + "</p>"
201
+ )
202
+ header_html += "</div>"
203
+ return header_html
204
+
205
+ def show_footer():
206
+ return (
207
+ "<div class='footer'>"
208
+ " <p>Need help? Contact our support team</p>"
209
+ " <p style='font-size: 0.8em; margin-top: 0.5rem;'>"
210
+ " CiviDoc AI Β© 2024"
211
+ " </p>"
212
+ "</div>"
213
+ =======
214
+ # utils/theme.py
215
+
216
+ def apply_dark_theme():
217
+ return (
218
+ "<style>"
219
+ # Base styles
220
+ "body { background-color: #0F172A; color: #E2E8F0; }"
221
+ ".main { padding: 0rem 1rem; }"
222
+
223
+ # Button styles
224
+ ".stButton>button {"
225
+ " width: 100%;"
226
+ " padding: 1rem;"
227
+ " font-size: 1.1em;"
228
+ " border-radius: 10px;"
229
+ " background-color: #1E293B;"
230
+ " color: #E2E8F0;"
231
+ " border: 1px solid #3B82F6;"
232
+ " box-shadow: 0 2px 5px rgba(0,0,0,0.3);"
233
+ " transition: all 0.2s ease;"
234
+ "}"
235
+ ".stButton>button:hover {"
236
+ " transform: translateY(-2px);"
237
+ " box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);"
238
+ " border-color: #60A5FA;"
239
+ " background-color: #2D3748;"
240
+ "}"
241
+
242
+ # Card styles
243
+ ".card {"
244
+ " padding: 1.5rem;"
245
+ " border-radius: 10px;"
246
+ " background-color: #1E293B;"
247
+ " margin-bottom: 1rem;"
248
+ " border: 1px solid #2D3748;"
249
+ " box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);"
250
+ " transition: all 0.2s ease;"
251
+ "}"
252
+ ".card:hover {"
253
+ " border-color: #3B82F6;"
254
+ " box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);"
255
+ " transform: translateY(-2px);"
256
+ "}"
257
+
258
+ # Typography
259
+ "h1, h2, h3, h4, h5, h6 { color: #60A5FA; }"
260
+ "h1 { font-size: 2.5em; font-weight: 700; }"
261
+ "h2 { font-size: 2em; font-weight: 600; }"
262
+ "h3 { font-size: 1.5em; font-weight: 600; }"
263
+ "p { line-height: 1.6; }"
264
+
265
+ # Form elements
266
+ ".stTextInput>div>div>input,"
267
+ ".stTextArea>div>div>textarea,"
268
+ ".stSelectbox>div>div>select {"
269
+ " background-color: #1E293B;"
270
+ " color: #E2E8F0;"
271
+ " border: 1px solid #3B82F6;"
272
+ " border-radius: 8px;"
273
+ "}"
274
+
275
+ # File uploader
276
+ ".stFileUploader>div>button {"
277
+ " background-color: #1E293B;"
278
+ " color: #E2E8F0;"
279
+ " border: 1px dashed #3B82F6;"
280
+ " border-radius: 8px;"
281
+ "}"
282
+ ".stFileUploader>div>button:hover {"
283
+ " border-color: #60A5FA;"
284
+ " background-color: #2D3748;"
285
+ "}"
286
+
287
+ # Expander
288
+ ".streamlit-expanderHeader {"
289
+ " background-color: #1E293B;"
290
+ " color: #E2E8F0;"
291
+ " border-radius: 8px;"
292
+ "}"
293
+ ".streamlit-expanderContent {"
294
+ " background-color: #1E293B;"
295
+ " border: 1px solid #2D3748;"
296
+ " border-radius: 0 0 8px 8px;"
297
+ "}"
298
+
299
+ # Tabs
300
+ ".stTabs [data-baseweb='tab-list'] {"
301
+ " gap: 8px;"
302
+ " background-color: #1E293B;"
303
+ " padding: 0.5rem;"
304
+ " border-radius: 8px;"
305
+ "}"
306
+ ".stTabs [data-baseweb='tab'] {"
307
+ " height: 50px;"
308
+ " background-color: #2D3748;"
309
+ " border-radius: 8px;"
310
+ " color: #E2E8F0;"
311
+ " border: 1px solid #3B82F6;"
312
+ "}"
313
+ ".stTabs [aria-selected='true'] {"
314
+ " background-color: #3B82F6;"
315
+ "}"
316
+
317
+ # Chat elements
318
+ ".chat-message {"
319
+ " padding: 1rem;"
320
+ " margin: 0.5rem 0;"
321
+ " border-radius: 8px;"
322
+ " background-color: #1E293B;"
323
+ " border: 1px solid #2D3748;"
324
+ "}"
325
+ ".user-message {"
326
+ " background-color: #2D3748;"
327
+ " border-color: #3B82F6;"
328
+ "}"
329
+
330
+ # Status indicators
331
+ ".status-badge {"
332
+ " display: inline-block;"
333
+ " padding: 0.25rem 0.75rem;"
334
+ " border-radius: 9999px;"
335
+ " font-size: 0.875rem;"
336
+ " font-weight: 500;"
337
+ " text-align: center;"
338
+ "}"
339
+ ".status-success { background-color: #065F46; color: #6EE7B7; }"
340
+ ".status-warning { background-color: #92400E; color: #FCD34D; }"
341
+ ".status-error { background-color: #991B1B; color: #FCA5A5; }"
342
+
343
+ # Tooltips
344
+ ".tooltip {"
345
+ " position: relative;"
346
+ " display: inline-block;"
347
+ "}"
348
+ ".tooltip .tooltiptext {"
349
+ " visibility: hidden;"
350
+ " background-color: #2D3748;"
351
+ " color: #E2E8F0;"
352
+ " text-align: center;"
353
+ " padding: 0.5rem;"
354
+ " border-radius: 6px;"
355
+ " border: 1px solid #3B82F6;"
356
+ " position: absolute;"
357
+ " z-index: 1;"
358
+ " bottom: 125%;"
359
+ " left: 50%;"
360
+ " transform: translateX(-50%);"
361
+ " opacity: 0;"
362
+ " transition: opacity 0.2s;"
363
+ "}"
364
+ ".tooltip:hover .tooltiptext {"
365
+ " visibility: visible;"
366
+ " opacity: 1;"
367
+ "}"
368
+
369
+ # Progress indicators
370
+ ".progress-bar {"
371
+ " width: 100%;"
372
+ " height: 8px;"
373
+ " background-color: #2D3748;"
374
+ " border-radius: 4px;"
375
+ " overflow: hidden;"
376
+ "}"
377
+ ".progress-bar-fill {"
378
+ " height: 100%;"
379
+ " background-color: #3B82F6;"
380
+ " transition: width 0.3s ease;"
381
+ "}"
382
+
383
+ # Custom scrollbar
384
+ "::-webkit-scrollbar { width: 8px; height: 8px; }"
385
+ "::-webkit-scrollbar-track { background: #1E293B; }"
386
+ "::-webkit-scrollbar-thumb { background: #3B82F6; border-radius: 4px; }"
387
+ "::-webkit-scrollbar-thumb:hover { background: #60A5FA; }"
388
+
389
+ # Animations
390
+ "@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }"
391
+ ".fade-in { animation: fadeIn 0.3s ease-in; }"
392
+
393
+ # Footer
394
+ ".footer {"
395
+ " text-align: center;"
396
+ " color: #94A3B8;"
397
+ " padding: 2rem 1rem;"
398
+ " margin-top: 2rem;"
399
+ " border-top: 1px solid #2D3748;"
400
+ "}"
401
+ "</style>"
402
+ )
403
+
404
+ def show_page_header(title, description=None):
405
+ header_html = (
406
+ "<div style='background-color: #1E293B; padding: 2rem; "+
407
+ "border-radius: 10px; margin-bottom: 2rem;'>"+
408
+ "<h1 style='color: #60A5FA; margin-bottom: 0.5rem;'>" + title + "</h1>"
409
+ )
410
+ if description:
411
+ header_html += (
412
+ "<p style='color: #E2E8F0; font-size: 1.1em;'>" + description + "</p>"
413
+ )
414
+ header_html += "</div>"
415
+ return header_html
416
+
417
+ def show_footer():
418
+ return (
419
+ "<div class='footer'>"
420
+ " <p>Need help? Contact our support team</p>"
421
+ " <p style='font-size: 0.8em; margin-top: 0.5rem;'>"
422
+ " CiviDoc AI Β© 2024"
423
+ " </p>"
424
+ "</div>"
425
+ >>>>>>> 1bc20a0d3edc7f88f03e506f84b01a7303d403b2
426
+ )
utils.py ADDED
@@ -0,0 +1,763 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <<<<<<< HEAD
2
+ import streamlit as st
3
+ from groq import Groq
4
+ import io
5
+ import base64
6
+ import re
7
+ import os
8
+ from dotenv import load_dotenv
9
+ from llama_index.core import VectorStoreIndex, Settings, Document
10
+ from llama_index.readers.file import PDFReader
11
+ from llama_index.llms.groq import Groq as LlamaGroq
12
+ from llama_index.embeddings.langchain import LangchainEmbedding
13
+ from langchain_community.embeddings import HuggingFaceEmbeddings
14
+ from datetime import datetime
15
+ from PIL import Image
16
+ import gettext
17
+
18
+ # Load environment variables and configure
19
+ load_dotenv()
20
+ groq_api_key = os.getenv("GROQ_API_KEY")
21
+ client = Groq(api_key=groq_api_key)
22
+
23
+ # Configure LlamaIndex
24
+ Settings.llm = LlamaGroq(api_key=groq_api_key, model="llama-3.1-70b-versatile")
25
+ lc_embed_model = HuggingFaceEmbeddings(
26
+ model_name="sentence-transformers/all-mpnet-base-v2"
27
+ )
28
+ Settings.embed_model = LangchainEmbedding(lc_embed_model)
29
+
30
+ def initialize_session_state():
31
+ """Initialize all session state variables"""
32
+ if 'chat_engines' not in st.session_state:
33
+ st.session_state.chat_engines = {}
34
+ if 'analyses' not in st.session_state:
35
+ st.session_state.analyses = {}
36
+ if 'documents' not in st.session_state:
37
+ st.session_state.documents = {}
38
+ if 'current_doc' not in st.session_state:
39
+ st.session_state.current_doc = None
40
+ if 'messages' not in st.session_state:
41
+ st.session_state.messages = []
42
+ if 'document_history' not in st.session_state:
43
+ st.session_state.document_history = {}
44
+
45
+ def encode_image_to_base64(image):
46
+ """Convert PIL Image to base64 string"""
47
+ buffered = io.BytesIO()
48
+ image.save(buffered, format="JPEG")
49
+ return base64.b64encode(buffered.getvalue()).decode()
50
+
51
+ def process_image(image):
52
+ """Process image using Llama vision model"""
53
+ img_base64 = encode_image_to_base64(image)
54
+ img_url = f"data:image/jpeg;base64,{img_base64}"
55
+
56
+ completion = client.chat.completions.create(
57
+ model="llama-3.2-11b-vision-preview",
58
+ messages=[
59
+ {
60
+ "role": "user",
61
+ "content": [
62
+ {
63
+ "type": "text",
64
+ "text": """Please analyze this government document and provide:
65
+ 1. Document type and purpose
66
+ 2. Key requirements and deadlines
67
+ 3. Complex terms explained simply
68
+ 4. Required actions or next steps
69
+ 5. Important contact information or submission details"""
70
+ },
71
+ {
72
+ "type": "image_url",
73
+ "image_url": {
74
+ "url": img_url
75
+ }
76
+ }
77
+ ]
78
+ }
79
+ ],
80
+ temperature=0.1,
81
+ max_tokens=1024,
82
+ top_p=1,
83
+ stream=False
84
+ )
85
+
86
+ return completion.choices[0].message.content
87
+
88
+ def generate_pdf_analysis(documents):
89
+ """Generate analysis from PDF documents using Groq"""
90
+ try:
91
+ # Combine all document content
92
+ full_text = "\n".join([doc.text for doc in documents])
93
+
94
+ # Generate analysis using Groq
95
+ completion = client.chat.completions.create(
96
+ model="llama-3.1-70b-versatile",
97
+ messages=[
98
+ {
99
+ "role": "user",
100
+ "content": (
101
+ "Please analyze this government document and provide:\n"
102
+ "1. Document Type and Purpose:\n"
103
+ " - What kind of document is this?\n"
104
+ " - What is its main purpose?\n\n"
105
+ "2. Key Requirements:\n"
106
+ " - What are the main requirements or conditions?\n"
107
+ " - What documents or information are needed?\n\n"
108
+ "3. Important Deadlines:\n"
109
+ " - What are the key dates and deadlines?\n"
110
+ " - Are there any time-sensitive requirements?\n\n"
111
+ "4. Complex Terms Explained:\n"
112
+ " - Explain any technical or legal terms in simple language\n"
113
+ " - Clarify any complex procedures\n\n"
114
+ "5. Required Actions:\n"
115
+ " - What steps need to be taken?\n"
116
+ " - What is the process to follow?\n\n"
117
+ "6. Contact Information:\n"
118
+ " - Who to contact for queries?\n"
119
+ " - Where to submit the documents?\n\n"
120
+ "Document content:\n" + full_text
121
+ )
122
+ }
123
+ ],
124
+ temperature=0.1,
125
+ max_tokens=2048,
126
+ top_p=1
127
+ )
128
+
129
+ # Format the analysis with proper styling
130
+ analysis = completion.choices[0].message.content
131
+
132
+ completionsum = client.chat.completions.create(
133
+ model="llama-3.1-8b-instant",
134
+ messages=[
135
+ {
136
+ "role": "user",
137
+ "content": (
138
+ "Summarize the following content: " + analysis
139
+ )
140
+ }
141
+ ],
142
+ temperature=0.1,
143
+ max_tokens=2048,
144
+ top_p=1
145
+ )
146
+
147
+ analysissum = completionsum.choices[0].message.content
148
+
149
+ return analysissum
150
+ except Exception as e:
151
+ error_msg = "Error generating PDF analysis: " + str(e)
152
+ raise Exception(error_msg)
153
+
154
+ def clean_llm_output(output):
155
+ """Clean LLM output by removing HTML tags and formatting symbols"""
156
+ # Remove HTML tags
157
+ cleaned_text = re.sub(r'<[^>]+>', '', output)
158
+ # Remove double asterisks
159
+ cleaned_text = cleaned_text.replace('**', '')
160
+ cleaned_text = cleaned_text.replace('*', '')
161
+ # Remove extra whitespace
162
+ cleaned_text = re.sub(r'\s+', ' ', cleaned_text)
163
+ return cleaned_text.strip()
164
+
165
+ def format_analysis_results(text):
166
+ """Format analysis results into structured HTML"""
167
+ # First clean the text
168
+ cleaned_text = clean_llm_output(text)
169
+
170
+ # Split into sections
171
+ sections = []
172
+ current_section = ""
173
+ current_title = ""
174
+
175
+ for line in cleaned_text.split('\n'):
176
+ line = line.strip()
177
+ if ':' in line and not line.startswith('*'):
178
+ # If we have a previous section, save it
179
+ if current_title:
180
+ sections.append((current_title, current_section.strip()))
181
+ # Start new section
182
+ parts = line.split(':', 1)
183
+ current_title = parts[0].strip()
184
+ current_section = parts[1].strip() if len(parts) > 1 else ""
185
+ else:
186
+ current_section += " " + line
187
+
188
+ # Add the last section
189
+ if current_title:
190
+ sections.append((current_title, current_section.strip()))
191
+
192
+ # Generate HTML
193
+ html = "<div class='analysis-results'>"
194
+ for title, content in sections:
195
+ html += f"""
196
+ <div class='analysis-section card' style='margin-bottom: 1rem;'>
197
+ <h4 style='color: #60A5FA; margin-bottom: 0.5rem;'>{title}</h4>
198
+ <p style='margin: 0;'>{content}</p>
199
+ </div>
200
+ """
201
+ html += "</div>"
202
+
203
+ return html
204
+
205
+ def process_captured_image(picture):
206
+ """Process image captured from camera with mobile-friendly UI"""
207
+ try:
208
+ # Show processing status
209
+ status_placeholder = st.empty()
210
+ status_placeholder.markdown(
211
+ "<div class='status-badge status-warning'>"
212
+ "πŸ“Έ Processing captured image..."
213
+ "</div>",
214
+ unsafe_allow_html=True
215
+ )
216
+
217
+ # Process the image
218
+ image = Image.open(picture)
219
+
220
+ # Display the captured image with proper mobile sizing
221
+ st.image(
222
+ image,
223
+ caption="Captured Document",
224
+ use_column_width=True # Makes image responsive
225
+ )
226
+
227
+ # Process image with AI
228
+ with st.spinner("Analyzing document..."):
229
+ analysis = process_image(image)
230
+
231
+ # Generate filename with timestamp
232
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
233
+ filename = f"captured_image_{timestamp}"
234
+
235
+ # Save results
236
+ st.session_state.analyses[filename] = {
237
+ 'type': 'image/jpeg',
238
+ 'analysis': analysis,
239
+ 'timestamp': datetime.datetime.now()
240
+ }
241
+
242
+ # Create chat engine
243
+ st.session_state.chat_engines[filename] = create_chat_engine(analysis)
244
+
245
+ # Save to history
246
+ save_to_history(
247
+ filename,
248
+ 'Captured Image',
249
+ analysis,
250
+ datetime.datetime.now()
251
+ )
252
+
253
+ # Update status to success
254
+ status_placeholder.markdown(
255
+ "<div class='status-badge status-success'>"
256
+ "βœ… Image analyzed successfully!"
257
+ "</div>",
258
+ unsafe_allow_html=True
259
+ )
260
+
261
+ # Display analysis results
262
+ st.markdown(
263
+ "<div class='card'>"
264
+ "<h4>Analysis Results</h4>"
265
+ f"<div style='margin: 1rem 0;'>{analysis}</div>"
266
+ "</div>",
267
+ unsafe_allow_html=True
268
+ )
269
+
270
+ # Mobile-friendly action buttons
271
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
272
+
273
+ col1, col2 = st.columns(2)
274
+ with col1:
275
+ if st.button("πŸ’¬ Start Chat", use_container_width=True):
276
+ st.session_state.current_doc = filename
277
+ st.switch_page("pages/2_πŸ’¬_Document_Chat.py")
278
+ with col2:
279
+ if st.button("πŸ“Έ New Capture", use_container_width=True):
280
+ st.rerun()
281
+
282
+ st.markdown("</div>", unsafe_allow_html=True)
283
+
284
+ except Exception as e:
285
+ st.error(
286
+ "❌ Error processing image\n"
287
+ f"Details: {str(e)}"
288
+ )
289
+
290
+ def process_pdf(pdf_file):
291
+ """Process PDF document using LlamaIndex"""
292
+ temp_dir = "temp_docs"
293
+ os.makedirs(temp_dir, exist_ok=True)
294
+ temp_path = os.path.join(temp_dir, "temp.pdf")
295
+
296
+ with open(temp_path, "wb") as f:
297
+ f.write(pdf_file.getvalue())
298
+
299
+ try:
300
+ reader = PDFReader()
301
+ documents = reader.load_data(temp_path)
302
+ return documents
303
+ finally:
304
+ if os.path.exists(temp_path):
305
+ os.remove(temp_path)
306
+ if os.path.exists(temp_dir) and not os.listdir(temp_dir):
307
+ os.rmdir(temp_dir)
308
+
309
+ def create_chat_engine(content):
310
+ """Create chat engine from document content"""
311
+ if isinstance(content, str):
312
+ documents = [Document(text=content)]
313
+ else:
314
+ documents = content
315
+
316
+ index = VectorStoreIndex.from_documents(documents)
317
+ return index.as_chat_engine(chat_mode="condense_question", verbose=True)
318
+
319
+ def generate_document(doc_type, fields):
320
+ """Generate government documents based on type and fields"""
321
+ prompt = f"""Generate a formal {doc_type} with the following details:
322
+
323
+ {fields}
324
+
325
+ Please format this as a proper official document following standard government formatting."""
326
+
327
+ completion = client.chat.completions.create(
328
+ model="llama-3.1-70b-versatile",
329
+ messages=[
330
+ {
331
+ "role": "user",
332
+ "content": prompt
333
+ }
334
+ ],
335
+ temperature=0.7,
336
+ max_tokens=2048,
337
+ top_p=1
338
+ )
339
+
340
+ return completion.choices[0].message.content
341
+
342
+ def save_to_history(doc_name, doc_type, content, timestamp=None):
343
+ """Save document to history with metadata"""
344
+ if timestamp is None:
345
+ timestamp = datetime.now()
346
+
347
+ st.session_state.document_history[doc_name] = {
348
+ 'type': doc_type,
349
+ 'content': content,
350
+ 'timestamp': timestamp,
351
+ 'status': 'Processed'
352
+ }
353
+
354
+ def get_document_history():
355
+ """Retrieve document history sorted by timestamp"""
356
+ history = st.session_state.document_history
357
+ return dict(sorted(
358
+ history.items(),
359
+ key=lambda x: x[1]['timestamp'],
360
+ reverse=True
361
+ ))
362
+
363
+ def delete_from_history(doc_name):
364
+ """Delete document from history"""
365
+ if doc_name in st.session_state.document_history:
366
+ del st.session_state.document_history[doc_name]
367
+ if doc_name in st.session_state.chat_engines:
368
+ del st.session_state.chat_engines[doc_name]
369
+ if doc_name in st.session_state.analyses:
370
+ del st.session_state.analyses[doc_name]
371
+ if st.session_state.current_doc == doc_name:
372
+ st.session_state.current_doc = None
373
+
374
+ def format_timestamp(timestamp):
375
+ """Format timestamp for display"""
376
+ return timestamp.strftime("%Y-%m-%d %H:%M:%S")
377
+ =======
378
+ # utils.py
379
+ import streamlit as st
380
+ from groq import Groq
381
+ import io
382
+ import base64
383
+ import re
384
+ import os
385
+ from dotenv import load_dotenv
386
+ from llama_index.core import VectorStoreIndex, Settings, Document
387
+ from llama_index.readers.file import PDFReader
388
+ from llama_index.llms.groq import Groq as LlamaGroq
389
+ from llama_index.embeddings.langchain import LangchainEmbedding
390
+ from langchain_community.embeddings import HuggingFaceEmbeddings
391
+ from datetime import datetime
392
+ from PIL import Image
393
+
394
+ # Load environment variables and configure
395
+ load_dotenv()
396
+ groq_api_key = os.getenv("GROQ_API_KEY")
397
+ client = Groq(api_key=groq_api_key)
398
+
399
+ # Configure LlamaIndex
400
+ Settings.llm = LlamaGroq(api_key=groq_api_key, model="llama-3.1-70b-versatile")
401
+ lc_embed_model = HuggingFaceEmbeddings(
402
+ model_name="sentence-transformers/all-mpnet-base-v2"
403
+ )
404
+ Settings.embed_model = LangchainEmbedding(lc_embed_model)
405
+
406
+ def initialize_session_state():
407
+ """Initialize all session state variables"""
408
+ if 'chat_engines' not in st.session_state:
409
+ st.session_state.chat_engines = {}
410
+ if 'analyses' not in st.session_state:
411
+ st.session_state.analyses = {}
412
+ if 'documents' not in st.session_state:
413
+ st.session_state.documents = {}
414
+ if 'current_doc' not in st.session_state:
415
+ st.session_state.current_doc = None
416
+ if 'messages' not in st.session_state:
417
+ st.session_state.messages = []
418
+ if 'document_history' not in st.session_state:
419
+ st.session_state.document_history = {}
420
+
421
+ def encode_image_to_base64(image):
422
+ """Convert PIL Image to base64 string"""
423
+ buffered = io.BytesIO()
424
+ image.save(buffered, format="JPEG")
425
+ return base64.b64encode(buffered.getvalue()).decode()
426
+
427
+ def process_image(image):
428
+ """Process image using Llama vision model"""
429
+ img_base64 = encode_image_to_base64(image)
430
+ img_url = f"data:image/jpeg;base64,{img_base64}"
431
+
432
+ completion = client.chat.completions.create(
433
+ model="llama-3.2-11b-vision-preview",
434
+ messages=[
435
+ {
436
+ "role": "user",
437
+ "content": [
438
+ {
439
+ "type": "text",
440
+ "text": """Please analyze this government document and provide:
441
+ 1. Document type and purpose
442
+ 2. Key requirements and deadlines
443
+ 3. Complex terms explained simply
444
+ 4. Required actions or next steps
445
+ 5. Important contact information or submission details"""
446
+ },
447
+ {
448
+ "type": "image_url",
449
+ "image_url": {
450
+ "url": img_url
451
+ }
452
+ }
453
+ ]
454
+ }
455
+ ],
456
+ temperature=0.1,
457
+ max_tokens=1024,
458
+ top_p=1,
459
+ stream=False
460
+ )
461
+
462
+ return completion.choices[0].message.content
463
+
464
+ def generate_pdf_analysis(documents):
465
+ """Generate analysis from PDF documents using Groq"""
466
+ try:
467
+ # Combine all document content
468
+ full_text = "\n".join([doc.text for doc in documents])
469
+
470
+ # Generate analysis using Groq
471
+ completion = client.chat.completions.create(
472
+ model="llama-3.1-70b-versatile",
473
+ messages=[
474
+ {
475
+ "role": "user",
476
+ "content": (
477
+ "Please analyze this government document and provide:\n"
478
+ "1. Document Type and Purpose:\n"
479
+ " - What kind of document is this?\n"
480
+ " - What is its main purpose?\n\n"
481
+ "2. Key Requirements:\n"
482
+ " - What are the main requirements or conditions?\n"
483
+ " - What documents or information are needed?\n\n"
484
+ "3. Important Deadlines:\n"
485
+ " - What are the key dates and deadlines?\n"
486
+ " - Are there any time-sensitive requirements?\n\n"
487
+ "4. Complex Terms Explained:\n"
488
+ " - Explain any technical or legal terms in simple language\n"
489
+ " - Clarify any complex procedures\n\n"
490
+ "5. Required Actions:\n"
491
+ " - What steps need to be taken?\n"
492
+ " - What is the process to follow?\n\n"
493
+ "6. Contact Information:\n"
494
+ " - Who to contact for queries?\n"
495
+ " - Where to submit the documents?\n\n"
496
+ "Document content:\n" + full_text
497
+ )
498
+ }
499
+ ],
500
+ temperature=0.1,
501
+ max_tokens=2048,
502
+ top_p=1
503
+ )
504
+
505
+ # Format the analysis with proper styling
506
+ analysis = completion.choices[0].message.content
507
+
508
+ completionsum = client.chat.completions.create(
509
+ model="llama-3.1-8b-instant",
510
+ messages=[
511
+ {
512
+ "role": "user",
513
+ "content": (
514
+ "Summarize the following content: " + analysis
515
+ )
516
+ }
517
+ ],
518
+ temperature=0.1,
519
+ max_tokens=2048,
520
+ top_p=1
521
+ )
522
+
523
+ analysis = completionsum.choices[0].message.content
524
+
525
+ # Add formatting for better readability
526
+ formatted_analysis = (
527
+ "<div class='analysis-container'>"
528
+ "<div class='analysis-section'>" +
529
+ analysis.replace('\n\n', '</div><div class="analysis-section">') +
530
+ "</div>"
531
+ "</div>"
532
+ )
533
+
534
+ return formatted_analysis
535
+
536
+ except Exception as e:
537
+ error_msg = "Error generating PDF analysis: " + str(e)
538
+ raise Exception(error_msg)
539
+
540
+ def clean_llm_output(output):
541
+ """Clean LLM output by removing HTML tags and formatting symbols"""
542
+ # Remove HTML tags
543
+ cleaned_text = re.sub(r'<[^>]+>', '', output)
544
+ # Remove double asterisks
545
+ cleaned_text = cleaned_text.replace('**', '')
546
+ cleaned_text = cleaned_text.replace('*', '')
547
+ # Remove extra whitespace
548
+ cleaned_text = re.sub(r'\s+', ' ', cleaned_text)
549
+ return cleaned_text.strip()
550
+
551
+ def format_analysis_results(text):
552
+ """Format analysis results into structured HTML"""
553
+ # First clean the text
554
+ cleaned_text = clean_llm_output(text)
555
+
556
+ # Split into sections
557
+ sections = []
558
+ current_section = ""
559
+ current_title = ""
560
+
561
+ for line in cleaned_text.split('\n'):
562
+ line = line.strip()
563
+ if ':' in line and not line.startswith('*'):
564
+ # If we have a previous section, save it
565
+ if current_title:
566
+ sections.append((current_title, current_section.strip()))
567
+ # Start new section
568
+ parts = line.split(':', 1)
569
+ current_title = parts[0].strip()
570
+ current_section = parts[1].strip() if len(parts) > 1 else ""
571
+ else:
572
+ current_section += " " + line
573
+
574
+ # Add the last section
575
+ if current_title:
576
+ sections.append((current_title, current_section.strip()))
577
+
578
+ # Generate HTML
579
+ html = "<div class='analysis-results'>"
580
+ for title, content in sections:
581
+ html += f"""
582
+ <div class='analysis-section card' style='margin-bottom: 1rem;'>
583
+ <h4 style='color: #60A5FA; margin-bottom: 0.5rem;'>{title}</h4>
584
+ <p style='margin: 0;'>{content}</p>
585
+ </div>
586
+ """
587
+ html += "</div>"
588
+
589
+ return html
590
+
591
+ def process_captured_image(picture):
592
+ """Process image captured from camera with mobile-friendly UI"""
593
+ try:
594
+ # Show processing status
595
+ status_placeholder = st.empty()
596
+ status_placeholder.markdown(
597
+ "<div class='status-badge status-warning'>"
598
+ "πŸ“Έ Processing captured image..."
599
+ "</div>",
600
+ unsafe_allow_html=True
601
+ )
602
+
603
+ # Process the image
604
+ image = Image.open(picture)
605
+
606
+ # Display the captured image with proper mobile sizing
607
+ st.image(
608
+ image,
609
+ caption="Captured Document",
610
+ use_column_width=True # Makes image responsive
611
+ )
612
+
613
+ # Process image with AI
614
+ with st.spinner("Analyzing document..."):
615
+ analysis = process_image(image)
616
+
617
+ # Generate filename with timestamp
618
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
619
+ filename = f"captured_image_{timestamp}"
620
+
621
+ # Save results
622
+ st.session_state.analyses[filename] = {
623
+ 'type': 'image/jpeg',
624
+ 'analysis': analysis,
625
+ 'timestamp': datetime.datetime.now()
626
+ }
627
+
628
+ # Create chat engine
629
+ st.session_state.chat_engines[filename] = create_chat_engine(analysis)
630
+
631
+ # Save to history
632
+ save_to_history(
633
+ filename,
634
+ 'Captured Image',
635
+ analysis,
636
+ datetime.datetime.now()
637
+ )
638
+
639
+ # Update status to success
640
+ status_placeholder.markdown(
641
+ "<div class='status-badge status-success'>"
642
+ "βœ… Image analyzed successfully!"
643
+ "</div>",
644
+ unsafe_allow_html=True
645
+ )
646
+
647
+ # Display analysis results
648
+ st.markdown(
649
+ "<div class='card'>"
650
+ "<h4>Analysis Results</h4>"
651
+ f"<div style='margin: 1rem 0;'>{analysis}</div>"
652
+ "</div>",
653
+ unsafe_allow_html=True
654
+ )
655
+
656
+ # Mobile-friendly action buttons
657
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
658
+
659
+ col1, col2 = st.columns(2)
660
+ with col1:
661
+ if st.button("πŸ’¬ Start Chat", use_container_width=True):
662
+ st.session_state.current_doc = filename
663
+ st.switch_page("pages/Document_Chat.py")
664
+ with col2:
665
+ if st.button("πŸ“Έ New Capture", use_container_width=True):
666
+ st.rerun()
667
+
668
+ st.markdown("</div>", unsafe_allow_html=True)
669
+
670
+ except Exception as e:
671
+ st.error(
672
+ "❌ Error processing image\n"
673
+ f"Details: {str(e)}"
674
+ )
675
+
676
+ def process_pdf(pdf_file):
677
+ """Process PDF document using LlamaIndex"""
678
+ temp_dir = "temp_docs"
679
+ os.makedirs(temp_dir, exist_ok=True)
680
+ temp_path = os.path.join(temp_dir, "temp.pdf")
681
+
682
+ with open(temp_path, "wb") as f:
683
+ f.write(pdf_file.getvalue())
684
+
685
+ try:
686
+ reader = PDFReader()
687
+ documents = reader.load_data(temp_path)
688
+ return documents
689
+ finally:
690
+ if os.path.exists(temp_path):
691
+ os.remove(temp_path)
692
+ if os.path.exists(temp_dir) and not os.listdir(temp_dir):
693
+ os.rmdir(temp_dir)
694
+
695
+ def create_chat_engine(content):
696
+ """Create chat engine from document content"""
697
+ if isinstance(content, str):
698
+ documents = [Document(text=content)]
699
+ else:
700
+ documents = content
701
+
702
+ index = VectorStoreIndex.from_documents(documents)
703
+ return index.as_chat_engine(chat_mode="condense_question", verbose=True)
704
+
705
+ def generate_document(doc_type, fields):
706
+ """Generate government documents based on type and fields"""
707
+ prompt = f"""Generate a formal {doc_type} with the following details:
708
+
709
+ {fields}
710
+
711
+ Please format this as a proper official document following standard government formatting."""
712
+
713
+ completion = client.chat.completions.create(
714
+ model="llama-3.1-70b-versatile",
715
+ messages=[
716
+ {
717
+ "role": "user",
718
+ "content": prompt
719
+ }
720
+ ],
721
+ temperature=0.7,
722
+ max_tokens=2048,
723
+ top_p=1
724
+ )
725
+
726
+ return completion.choices[0].message.content
727
+
728
+ def save_to_history(doc_name, doc_type, content, timestamp=None):
729
+ """Save document to history with metadata"""
730
+ if timestamp is None:
731
+ timestamp = datetime.now()
732
+
733
+ st.session_state.document_history[doc_name] = {
734
+ 'type': doc_type,
735
+ 'content': content,
736
+ 'timestamp': timestamp,
737
+ 'status': 'Processed'
738
+ }
739
+
740
+ def get_document_history():
741
+ """Retrieve document history sorted by timestamp"""
742
+ history = st.session_state.document_history
743
+ return dict(sorted(
744
+ history.items(),
745
+ key=lambda x: x[1]['timestamp'],
746
+ reverse=True
747
+ ))
748
+
749
+ def delete_from_history(doc_name):
750
+ """Delete document from history"""
751
+ if doc_name in st.session_state.document_history:
752
+ del st.session_state.document_history[doc_name]
753
+ if doc_name in st.session_state.chat_engines:
754
+ del st.session_state.chat_engines[doc_name]
755
+ if doc_name in st.session_state.analyses:
756
+ del st.session_state.analyses[doc_name]
757
+ if st.session_state.current_doc == doc_name:
758
+ st.session_state.current_doc = None
759
+
760
+ def format_timestamp(timestamp):
761
+ """Format timestamp for display"""
762
+ return timestamp.strftime("%Y-%m-%d %H:%M:%S")
763
+ >>>>>>> 1bc20a0d3edc7f88f03e506f84b01a7303d403b2
πŸ›οΈ_CiviDoc_AI.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from theme import apply_dark_theme, show_page_header, show_footer
3
+
4
+ # Page config
5
+ st.set_page_config(
6
+ page_title="CiviDoc AI",
7
+ page_icon="πŸ›οΈ",
8
+ layout="centered",
9
+ initial_sidebar_state="collapsed"
10
+ )
11
+
12
+ # Apply dark theme
13
+ st.markdown(apply_dark_theme(), unsafe_allow_html=True)
14
+
15
+ def main():
16
+ # Header
17
+ st.markdown(show_page_header(
18
+ "<div style='text-align: center;'>"
19
+ "πŸ›οΈ CiviDoc AI",
20
+ "<div style='text-align: center;'>"
21
+ "Your AI-powered companion for all government document needs",
22
+ ), unsafe_allow_html=True)
23
+
24
+ # Quick Access Section - Mobile Friendly Cards
25
+ st.markdown(
26
+ "<div class='grid'>" # Uses the responsive grid system
27
+
28
+ "<div class='card' onclick='void(0)'>" # Added onclick for better touch feedback
29
+ "<div style='text-align: center;'>"
30
+ "<h3 style='margin-bottom: 0.5rem;'>πŸ“ Document Analysis</h3>"
31
+ "<p style='margin-bottom: 1rem;'>Upload and understand government documents instantly</p>"
32
+ "<div class='status-badge status-success'>Ready to Use</div>"
33
+ "</div>"
34
+ "</div>"
35
+
36
+ "<div class='card' onclick='void(0)'>"
37
+ "<div style='text-align: center;'>"
38
+ "<h3 style='margin-bottom: 0.5rem;'>✍️ Writing Assistant</h3>"
39
+ "<p style='margin-bottom: 1rem;'>Create professional government documents effortlessly</p>"
40
+ "<div class='status-badge status-success'>Ready to Use</div>"
41
+ "</div>"
42
+ "</div>"
43
+
44
+ "<div class='card' onclick='void(0)'>"
45
+ "<div style='text-align: center;'>"
46
+ "<h3 style='margin-bottom: 0.5rem;'>πŸ’¬ Document Chat</h3>"
47
+ "<p style='margin-bottom: 1rem;'>Get instant answers about your documents</p>"
48
+ "<div class='status-badge status-success'>Ready to Use</div>"
49
+ "</div>"
50
+ "</div>"
51
+
52
+ "</div>",
53
+ unsafe_allow_html=True
54
+ )
55
+
56
+ # Quick Action Buttons - Touch Friendly
57
+ st.markdown("<div class='touch-spacing'>", unsafe_allow_html=True)
58
+ if st.button("πŸ“ Start Document Analysis", use_container_width=True):
59
+ st.switch_page("pages/1_πŸ“_Document_Analysis.py")
60
+ if st.button("✍️ Create New Document", use_container_width=True):
61
+ st.switch_page("pages/3_✍️_Writing_Assistant.py")
62
+ if st.button("πŸ’¬ Open Document Chat", use_container_width=True):
63
+ st.switch_page("pages/2_πŸ’¬_Document_Chat.py")
64
+ st.markdown("</div>", unsafe_allow_html=True)
65
+
66
+ # Features Section - Responsive Grid
67
+ st.markdown("<h3 style='margin: 1.5rem 0 1rem;'>🌟 Key Features</h3>", unsafe_allow_html=True)
68
+
69
+ # Features grid with improved mobile layout
70
+ st.markdown(
71
+ "<div class='grid'>"
72
+
73
+ # Document Analysis Card
74
+ "<div class='card'>"
75
+ "<h3 style='margin-bottom: 0.75rem;'>πŸ“‘ Document Analysis</h3>"
76
+ "<div class='touch-spacing'>"
77
+ "<p>βœ“ Instant document understanding</p>"
78
+ "<p>βœ“ Complex term explanations</p>"
79
+ "<p>βœ“ Form filling guidance</p>"
80
+ "<p>βœ“ Requirement extraction</p>"
81
+ "<p>βœ“ Deadline tracking</p>"
82
+ "</div>"
83
+ "</div>"
84
+
85
+ # Writing Assistant Card
86
+ "<div class='card'>"
87
+ "<h3 style='margin-bottom: 0.75rem;'>✍️ Writing Assistant</h3>"
88
+ "<div class='touch-spacing'>"
89
+ "<p>βœ“ RTI application generator</p>"
90
+ "<p>βœ“ Complaint letter creator</p>"
91
+ "<p>βœ“ Legal notice drafting</p>"
92
+ "<p>βœ“ Appeal letter formatting</p>"
93
+ "<p>βœ“ Custom document templates</p>"
94
+ "</div>"
95
+ "</div>"
96
+
97
+ # Interactive Help Card
98
+ "<div class='card'>"
99
+ "<h3 style='margin-bottom: 0.75rem;'>πŸ’¬ Interactive Help</h3>"
100
+ "<div class='touch-spacing'>"
101
+ "<p>βœ“ Real-time document chat</p>"
102
+ "<p>βœ“ Context-aware responses</p>"
103
+ "<p>βœ“ Procedure explanations</p>"
104
+ "<p>βœ“ Multi-document support</p>"
105
+ "<p>βœ“ Instant clarifications</p>"
106
+ "</div>"
107
+ "</div>"
108
+
109
+ # Document Management Card
110
+ "<div class='card'>"
111
+ "<h3 style='margin-bottom: 0.75rem;'>πŸ“š Document Management</h3>"
112
+ "<div class='touch-spacing'>"
113
+ "<p>βœ“ Secure document storage</p>"
114
+ "<p>βœ“ Version tracking</p>"
115
+ "<p>βœ“ Easy organization</p>"
116
+ "<p>βœ“ Quick retrieval</p>"
117
+ "<p>βœ“ Status monitoring</p>"
118
+ "</div>"
119
+ "</div>"
120
+
121
+ "</div>",
122
+ unsafe_allow_html=True
123
+ )
124
+
125
+ # How It Works Section - Mobile Friendly Steps
126
+ st.markdown("<h3 style='margin: 1.5rem 0 1rem;'>πŸ”„ How It Works</h3>", unsafe_allow_html=True)
127
+
128
+ st.markdown(
129
+ "<div class='grid'>" # Responsive grid
130
+
131
+ # Step 1
132
+ "<div class='card' style='text-align: center;'>"
133
+ "<h4 style='margin-bottom: 0.5rem;'>1. Upload</h4>"
134
+ "<p style='margin-bottom: 0.75rem;'>Upload your government documents or start creating new ones</p>"
135
+ "<div class='progress-bar'><div class='progress-bar-fill' style='width: 25%;'></div></div>"
136
+ "</div>"
137
+
138
+ # Step 2
139
+ "<div class='card' style='text-align: center;'>"
140
+ "<h4 style='margin-bottom: 0.5rem;'>2. Process</h4>"
141
+ "<p style='margin-bottom: 0.75rem;'>Our AI analyzes and processes your documents instantly</p>"
142
+ "<div class='progress-bar'><div class='progress-bar-fill' style='width: 50%;'></div></div>"
143
+ "</div>"
144
+
145
+ # Step 3
146
+ "<div class='card' style='text-align: center;'>"
147
+ "<h4 style='margin-bottom: 0.5rem;'>3. Understand</h4>"
148
+ "<p style='margin-bottom: 0.75rem;'>Get clear explanations and guidance for your documents</p>"
149
+ "<div class='progress-bar'><div class='progress-bar-fill' style='width: 75%;'></div></div>"
150
+ "</div>"
151
+
152
+ # Step 4
153
+ "<div class='card' style='text-align: center;'>"
154
+ "<h4 style='margin-bottom: 0.5rem;'>4. Act</h4>"
155
+ "<p style='margin-bottom: 0.75rem;'>Take action with confidence using our recommendations</p>"
156
+ "<div class='progress-bar'><div class='progress-bar-fill' style='width: 100%;'></div></div>"
157
+ "</div>"
158
+
159
+ "</div>",
160
+ unsafe_allow_html=True
161
+ )
162
+
163
+ # Additional Information - Mobile Friendly Layout
164
+ st.markdown("<div class='grid'>", unsafe_allow_html=True)
165
+
166
+ # Who Is This For Section
167
+ st.markdown(
168
+ "<div class='card'>"
169
+ "<h3 style='margin-bottom: 0.75rem;'>🎯 Who Is This For?</h3>"
170
+ "<div class='touch-spacing'>"
171
+ "<p>β€’ Citizens dealing with government procedures</p>"
172
+ "<p>β€’ RTI applicants and activists</p>"
173
+ "<p>β€’ Legal professionals</p>"
174
+ "<p>β€’ Government service seekers</p>"
175
+ "<p>β€’ Anyone needing document assistance</p>"
176
+ "</div>"
177
+ "</div>",
178
+ unsafe_allow_html=True
179
+ )
180
+
181
+ # Security & Privacy Section
182
+ st.markdown(
183
+ "<div class='card'>"
184
+ "<h3 style='margin-bottom: 0.75rem;'>πŸ›‘οΈ Security & Privacy</h3>"
185
+ "<div class='touch-spacing'>"
186
+ "<p>β€’ End-to-end encryption</p>"
187
+ "<p>β€’ Secure document processing</p>"
188
+ "<p>β€’ No permanent storage</p>"
189
+ "<p>β€’ Privacy-first approach</p>"
190
+ "<p>β€’ Regular security updates</p>"
191
+ "</div>"
192
+ "</div>",
193
+ unsafe_allow_html=True
194
+ )
195
+
196
+ st.markdown("</div>", unsafe_allow_html=True)
197
+
198
+ # Footer
199
+ st.markdown(show_footer(), unsafe_allow_html=True)
200
+
201
+ if __name__ == "__main__":
202
+ main()