mohsin-devs commited on
Commit
bc939ab
·
1 Parent(s): 4172042

Deploy BankBot AI project files

Browse files
Files changed (8) hide show
  1. .streamlit/config.toml +6 -0
  2. README.md +29 -13
  3. app.py +920 -0
  4. data/intents.json +326 -0
  5. ollama_integration.py +201 -0
  6. requirements.txt +0 -0
  7. users.json +1 -0
  8. utils.py +231 -0
.streamlit/config.toml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ [theme]
2
+ base="light"
3
+ primaryColor="#1E40AF"
4
+ backgroundColor="#F8FAFC"
5
+ secondaryBackgroundColor="#F1F5F9"
6
+ textColor="#0F172A"
README.md CHANGED
@@ -1,20 +1,36 @@
1
  ---
2
- title: BankBot AI
3
- emoji: 🚀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
  pinned: false
11
- short_description: AI-powered banking assistant
12
  license: mit
13
  ---
14
 
15
- # Welcome to Streamlit!
16
 
17
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
18
 
19
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
20
- forums](https://discuss.streamlit.io).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Central Bank AI
3
+ emoji: 🏦
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: streamlit
7
+ sdk_version: 1.54.0
8
+ app_file: app.py
 
9
  pinned: false
 
10
  license: mit
11
  ---
12
 
13
+ # 🏦 Central Bank AI — BankBot
14
 
15
+ A professional AI-powered banking assistant built with Streamlit.
16
 
17
+ ## Features
18
+ - 💬 Banking chatbot powered by **Groq AI** (cloud) or **Ollama** (local)
19
+ - 📊 Financial dashboard with transaction history and analytics
20
+ - 🔐 User authentication with session management
21
+ - 📋 FAQ-based instant responses from a structured intents database
22
+
23
+ ## AI Backend
24
+ - **Cloud (HF Spaces):** Uses [Groq AI](https://console.groq.com) — set `GROQ_API_KEY` as a Space Secret
25
+ - **Local:** Falls back to [Ollama](https://ollama.com) (llama3) automatically
26
+
27
+ ## Setup (Local)
28
+ ```bash
29
+ pip install -r requirements.txt
30
+ ollama pull llama3
31
+ streamlit run app.py
32
+ ```
33
+
34
+ ## Setup (Hugging Face Spaces)
35
+ 1. Add `GROQ_API_KEY` as a **Secret** in Space Settings
36
+ 2. The app will automatically use Groq AI
app.py ADDED
@@ -0,0 +1,920 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.express as px
5
+ import time
6
+ from utils import (
7
+ validate_email,
8
+ validate_password_strength,
9
+ format_currency,
10
+ get_timestamp,
11
+ save_chat_session,
12
+ load_chat_session,
13
+ delete_chat_session,
14
+ clear_all_chat_history,
15
+ persist_user,
16
+ get_persisted_users,
17
+ save_active_session,
18
+ get_active_session,
19
+ clear_active_session,
20
+ get_ai_response,
21
+ stream_ai_response,
22
+ check_ollama_connection,
23
+ get_active_backend,
24
+ get_all_chat_sessions,
25
+ get_faq_response,
26
+ rewrite_banking_response,
27
+ is_banking_query
28
+ )
29
+
30
+ st.set_page_config(
31
+ page_title="Central Bank AI",
32
+ page_icon="🏦",
33
+ layout="wide",
34
+ initial_sidebar_state="expanded"
35
+ )
36
+
37
+ def apply_custom_style(theme="dark"):
38
+ # Define color palette based on theme
39
+ if theme == "dark":
40
+ colors = {
41
+ "bg": "#0B1220",
42
+ "card_bg": "#111827",
43
+ "text": "#f1f5f9",
44
+ "text_secondary": "#94a3b8",
45
+ "primary": "#2563EB",
46
+ "secondary": "#0ea5e9",
47
+ "border": "#1F2937",
48
+ "input_bg": "rgba(30, 41, 59, 0.8)",
49
+ "shadow": "rgba(0, 0, 0, 0.4)",
50
+ "success": "#10B981",
51
+ "warning": "#f59e0b",
52
+ "danger": "#EF4444",
53
+ "sidebar_bg": "#0F172A",
54
+ "hover": "rgba(255, 255, 255, 0.05)"
55
+ }
56
+ else:
57
+ colors = {
58
+ "bg": "#F8FAFC",
59
+ "card_bg": "#FFFFFF",
60
+ "text": "#0F172A",
61
+ "text_secondary": "#64748B",
62
+ "primary": "#1E40AF",
63
+ "secondary": "#2563EB",
64
+ "border": "#E2E8F0",
65
+ "input_bg": "#F8FAFC",
66
+ "shadow": "rgba(0, 0, 0, 0.04)",
67
+ "success": "#10B981",
68
+ "warning": "#d97706",
69
+ "danger": "#EF4444",
70
+ "sidebar_bg": "#F1F5F9",
71
+ "hover": "#EFF6FF"
72
+ }
73
+
74
+ st.session_state.colors = colors
75
+
76
+ st.markdown(f"""
77
+ <style>
78
+ /* Import Fonts */
79
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@500;600;700&display=swap');
80
+
81
+ /* Global Reset & Typography */
82
+ .stApp {{
83
+ font-family: 'Inter', sans-serif;
84
+ color: {colors['text']};
85
+ background-color: {colors['bg']};
86
+ }}
87
+
88
+ h1, h2, h3, h4, h5, h6 {{
89
+ font-family: 'Poppins', sans-serif;
90
+ font-weight: 600;
91
+ color: {colors['text']};
92
+ margin-bottom: 0.5rem;
93
+ }}
94
+
95
+ h1 {{ font-size: 32px !important; }}
96
+ h2 {{ font-size: 24px !important; }}
97
+ h3 {{ font-size: 18px !important; }}
98
+
99
+ /* Layout Logic */
100
+ .main .block-container {{
101
+ padding-top: 5rem !important;
102
+ padding-bottom: 2rem !important;
103
+ max-width: 1400px;
104
+ margin: 0 auto;
105
+ }}
106
+
107
+ header[data-testid="stHeader"] {{
108
+ background: transparent !important;
109
+ box-shadow: none !important;
110
+ height: 0 !important;
111
+ }}
112
+
113
+ [data-testid="stAppToolbar"] {{
114
+ display: none !important;
115
+ }}
116
+
117
+ [data-testid="stSidebarCollapseButton"] {{
118
+ visibility: hidden !important;
119
+ opacity: 0 !important;
120
+ }}
121
+
122
+ /* Footer still hidden */
123
+ footer {{
124
+ display: none !important;
125
+ }}
126
+
127
+ /* Global Sidebar Styling */
128
+ /* Global Sidebar Styling - Force visibility and width */
129
+ section[data-testid="stSidebar"] {{
130
+ background: {colors.get('sidebar_bg', '#F1F5F9')} !important;
131
+ border-right: 1px solid {colors['border']} !important;
132
+ min-width: 320px !important;
133
+ width: 320px !important;
134
+ visibility: visible !important;
135
+ display: block !important;
136
+ }}
137
+
138
+ /* Ensure child containers allow visibility */
139
+ [data-testid="stSidebarContent"] {{
140
+ visibility: visible !important;
141
+ }}
142
+
143
+ /* Hide the 'collapsed' hamburger icon in the top left if it appears */
144
+ [data-testid="collapsedControl"] {{
145
+ display: none !important;
146
+ }}
147
+
148
+ /* Remove default sidebar top padding */
149
+ section[data-testid="stSidebar"] > div:first-child {{
150
+ padding-top: 2rem !important;
151
+ }}
152
+
153
+ /* Custom Scrollbar */
154
+ ::-webkit-scrollbar {{
155
+ width: 6px;
156
+ }}
157
+ ::-webkit-scrollbar-thumb {{
158
+ background: {colors['border']};
159
+ border-radius: 10px;
160
+ }}
161
+
162
+ /* Section Titles */
163
+ .section-title {{
164
+ font-size: 11px;
165
+ letter-spacing: 0.08em;
166
+ text-transform: uppercase;
167
+ color: #64748b;
168
+ margin-bottom: 8px;
169
+ margin-top: 24px;
170
+ }}
171
+
172
+ /* User Card */
173
+ .user-card {{
174
+ background: {colors['card_bg']};
175
+ padding: 14px;
176
+ border-radius: 12px;
177
+ border: 1px solid {colors['border']};
178
+ margin-bottom: 24px;
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 12px;
182
+ transition: all 0.2s ease;
183
+ }}
184
+ .user-card:hover {{
185
+ transform: translateX(2px);
186
+ background: {colors.get('hover', 'rgba(255,255,255,0.05)')};
187
+ }}
188
+ .user-avatar {{
189
+ background: {colors['primary']};
190
+ width: 36px;
191
+ height: 36px;
192
+ border-radius: 50%;
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ color: white;
197
+ font-weight: bold;
198
+ }}
199
+ .user-name {{
200
+ font-weight: 600;
201
+ font-size: 14px;
202
+ color: {colors['text']};
203
+ }}
204
+ .user-email {{
205
+ font-size: 12px;
206
+ color: {colors['text_secondary']};
207
+ }}
208
+
209
+ /* Navigation / Sidebar Buttons */
210
+ .sidebar-btn {{
211
+ padding: 10px 14px;
212
+ border-radius: 8px;
213
+ color: #cbd5e1;
214
+ transition: all 0.2s ease;
215
+ margin-bottom: 8px;
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 8px;
219
+ cursor: pointer;
220
+ }}
221
+ .sidebar-btn:hover {{
222
+ background-color: {colors.get('hover', 'rgba(255,255,255,0.05)')};
223
+ color: white;
224
+ transform: translateX(2px);
225
+ }}
226
+ .sidebar-active {{
227
+ background-color: rgba(37, 99, 235, 0.15);
228
+ border-left: 3px solid {colors['primary']};
229
+ color: white;
230
+ border-radius: 0 8px 8px 0;
231
+ }}
232
+
233
+ /* Chat History */
234
+ .chat-history-container {{
235
+ max-height: 250px;
236
+ overflow-y: auto;
237
+ margin-bottom: 24px;
238
+ padding-right: 4px;
239
+ }}
240
+ .chat-item {{
241
+ padding: 8px 10px;
242
+ border-radius: 8px;
243
+ color: #cbd5e1;
244
+ transition: all 0.2s ease;
245
+ font-size: 0.9rem;
246
+ margin-bottom: 4px;
247
+ cursor: pointer;
248
+ white-space: nowrap;
249
+ overflow: hidden;
250
+ text-overflow: ellipsis;
251
+ }}
252
+ .chat-item:hover {{
253
+ background: {colors.get('hover', 'rgba(255,255,255,0.05)')};
254
+ color: white;
255
+ transform: translateX(2px);
256
+ }}
257
+
258
+ /* Logout Button */
259
+ .logout-btn button {{
260
+ background: transparent !important;
261
+ border: 1px solid {colors['danger']} !important;
262
+ color: {colors['danger']} !important;
263
+ border-radius: 8px !important;
264
+ transition: all 0.2s ease !important;
265
+ width: 100%;
266
+ padding: 8px !important;
267
+ }}
268
+ .logout-btn button:hover {{
269
+ background: {colors['danger']} !important;
270
+ color: white !important;
271
+ }}
272
+
273
+ /* AI Status Badge */
274
+ .status-badge {{
275
+ padding: 8px 12px;
276
+ border-radius: 8px;
277
+ font-size: 13px;
278
+ font-weight: 600;
279
+ display: flex;
280
+ align-items: center;
281
+ gap: 8px;
282
+ margin-top: 8px;
283
+ border: 1px solid {colors['border']};
284
+ }}
285
+
286
+ .status-online {{
287
+ background: rgba(16, 185, 129, 0.1);
288
+ color: #10b981;
289
+ border-color: rgba(16, 185, 129, 0.2);
290
+ }}
291
+
292
+ .status-offline {{
293
+ background: rgba(239, 68, 68, 0.1);
294
+ color: #ef4444;
295
+ border-color: rgba(239, 68, 68, 0.2);
296
+ }}
297
+
298
+ /* Card Component */
299
+ .bank-card {{
300
+ background-color: {colors['card_bg']};
301
+ border: 1px solid {colors['border']};
302
+ border-radius: 14px;
303
+ padding: 24px;
304
+ box-shadow: 0 4px 15px {colors['shadow']};
305
+ margin-bottom: 16px;
306
+ }}
307
+
308
+ /* Primary Buttons */
309
+ .stButton>button {{
310
+ border-radius: 8px;
311
+ transition: 0.3s ease;
312
+ }}
313
+
314
+ .stButton>button:hover {{
315
+ transform: translateY(-2px);
316
+ }}
317
+
318
+ /* Chat Interface Styling */
319
+ .user-bubble {{
320
+ background: {colors['primary']};
321
+ color: white;
322
+ padding: 12px 20px;
323
+ border-radius: 20px 20px 4px 20px;
324
+ margin-bottom: 12px;
325
+ max-width: 85%;
326
+ margin-left: auto;
327
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
328
+ font-size: 0.95rem;
329
+ line-height: 1.5;
330
+ }}
331
+
332
+ .ai-bubble {{
333
+ background: {colors['card_bg']};
334
+ color: {colors['text']};
335
+ padding: 12px 20px;
336
+ border-radius: 20px 20px 20px 4px;
337
+ margin-bottom: 12px;
338
+ max-width: 85%;
339
+ border: 1px solid {colors['border']};
340
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
341
+ font-size: 0.95rem;
342
+ line-height: 1.5;
343
+ }}
344
+
345
+ /* Modern Chat Input Styling */
346
+ section[data-testid="stChatInput"] {{
347
+ padding-bottom: 2rem !important;
348
+ }}
349
+
350
+ section[data-testid="stChatInput"] > div {{
351
+ background-color: transparent !important;
352
+ }}
353
+
354
+ section[data-testid="stChatInput"] textarea {{
355
+ background-color: {colors['input_bg']} !important;
356
+ border: 1px solid {colors['border']} !important;
357
+ border-radius: 25px !important;
358
+ padding: 12px 20px !important;
359
+ color: {colors['text']} !important;
360
+ box-shadow: 0 4px 12px {colors['shadow']} !important;
361
+ transition: all 0.3s ease;
362
+ }}
363
+
364
+ section[data-testid="stChatInput"] textarea:focus {{
365
+ border-color: {colors['primary']} !important;
366
+ box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3) !important;
367
+ }}
368
+
369
+ /* Disable default Streamlit fading on rapid updates */
370
+ div[data-testid="stVerticalBlock"] > div,
371
+ div[data-testid="stVerticalBlock"],
372
+ div.element-container,
373
+ div.stMarkdown,
374
+ div[data-testid="stMarkdownContainer"],
375
+ div[data-testid="stChatMessage"] {{
376
+ transition: none !important;
377
+ animation: none !important;
378
+ opacity: 1 !important;
379
+ }}
380
+
381
+ * {{
382
+ animation: none !important;
383
+ }}
384
+
385
+ </style>
386
+ """, unsafe_allow_html=True)
387
+
388
+ def init_session_state():
389
+ if "users" not in st.session_state:
390
+ st.session_state.users = get_persisted_users()
391
+
392
+ if "logged_in" not in st.session_state:
393
+ last_user = get_active_session()
394
+ if last_user and last_user in st.session_state.users:
395
+ st.session_state.logged_in = True
396
+ st.session_state.username = last_user
397
+ st.session_state.email = st.session_state.users[last_user]["email"]
398
+ else:
399
+ st.session_state.logged_in = False
400
+
401
+ if "theme" not in st.session_state:
402
+ st.session_state.theme = "light"
403
+
404
+ apply_custom_style(st.session_state.theme)
405
+
406
+ if "username" not in st.session_state:
407
+ st.session_state.username = ""
408
+ if "email" not in st.session_state:
409
+ st.session_state.email = ""
410
+ if "current_page" not in st.session_state:
411
+ st.session_state.current_page = "login"
412
+ if "chat_sessions" not in st.session_state:
413
+ if st.session_state.logged_in and st.session_state.username:
414
+ st.session_state.chat_sessions = get_all_chat_sessions(st.session_state.username)
415
+ else:
416
+ st.session_state.chat_sessions = []
417
+ if "current_chat_id" not in st.session_state:
418
+ st.session_state.current_chat_id = None
419
+ if "messages" not in st.session_state:
420
+ st.session_state.messages = []
421
+ if "balance" not in st.session_state:
422
+ st.session_state.balance = 850000.00
423
+ if "interest_rate" not in st.session_state:
424
+ st.session_state.interest_rate = 6.5
425
+ if "accrued_interest" not in st.session_state:
426
+ st.session_state.accrued_interest = 55000.00
427
+ if "active_loans" not in st.session_state:
428
+ st.session_state.active_loans = 2
429
+ if "total_loan_amount" not in st.session_state:
430
+ st.session_state.total_loan_amount = 3500000.00
431
+
432
+ init_session_state()
433
+
434
+ def login(username, password):
435
+ if username in st.session_state.users:
436
+ if st.session_state.users[username]["password"] == password:
437
+ st.session_state.logged_in = True
438
+ st.session_state.username = username
439
+ st.session_state.email = st.session_state.users[username]["email"]
440
+ st.session_state.current_page = "dashboard"
441
+ st.session_state.chat_sessions = get_all_chat_sessions(username)
442
+ save_active_session(username)
443
+ return True
444
+ return False
445
+
446
+ def signup(username, email, password):
447
+ if username in st.session_state.users:
448
+ return False, "Username already exists"
449
+
450
+ st.session_state.users[username] = {
451
+ "email": email,
452
+ "password": password
453
+ }
454
+ persist_user(username, email, password)
455
+ return True, "Account created successfully!"
456
+
457
+ def logout():
458
+ st.session_state.logged_in = False
459
+ st.session_state.username = ""
460
+ st.session_state.email = ""
461
+ st.session_state.current_page = "login"
462
+ st.session_state.messages = []
463
+ st.session_state.current_chat_id = None
464
+ clear_active_session()
465
+
466
+ def get_mock_transactions():
467
+ dates = pd.date_range(end=pd.Timestamp.now(), periods=30, freq='D')
468
+
469
+ types = []
470
+ amounts = []
471
+ cats = []
472
+ for _ in range(30):
473
+ if np.random.random() > 0.8:
474
+ types.append("Income")
475
+ cats.append(np.random.choice(["Salary", "Investment", "Refund"]))
476
+ amounts.append(round(float(np.random.uniform(5000, 25000)), 2))
477
+ else:
478
+ types.append("Expense")
479
+ cats.append(np.random.choice(["Food", "Rent", "Shopping", "Transport", "Entertainment", "Utilities"]))
480
+ amounts.append(round(float(np.random.uniform(100, 5000)), 2))
481
+
482
+ data = {"Date": dates, "Category": cats, "Type": types, "Amount": amounts}
483
+ return pd.DataFrame(data)
484
+
485
+ def show_login_page():
486
+ col1, col2, col3 = st.columns([1, 2, 1])
487
+ with col2:
488
+ st.title("🏦 Central Bank AI")
489
+ st.subheader("Login")
490
+ st.divider()
491
+
492
+ with st.form("login_form"):
493
+ username = st.text_input("Username", placeholder="Enter your username")
494
+ password = st.text_input("Password", type="password", placeholder="Enter your password")
495
+ submit = st.form_submit_button("Login", use_container_width=True, type="primary")
496
+
497
+ if submit:
498
+ if login(username, password):
499
+ st.success("Login successful!")
500
+ st.rerun()
501
+ else:
502
+ st.error("Invalid username or password")
503
+
504
+ st.divider()
505
+ if st.button("Don't have an account? Sign Up", use_container_width=True):
506
+ st.session_state.current_page = "signup"
507
+ st.rerun()
508
+
509
+ def show_signup_page():
510
+ col1, col2, col3 = st.columns([1, 2, 1])
511
+ with col2:
512
+ st.title("🏦 Central Bank AI")
513
+ st.subheader("Create Account")
514
+ st.divider()
515
+
516
+ with st.form("signup_form"):
517
+ username = st.text_input("Username", placeholder="Choose a username")
518
+ email = st.text_input("Email", placeholder="Enter your email")
519
+ password = st.text_input("Password", type="password", placeholder="Create a password")
520
+ confirm_password = st.text_input("Confirm Password", type="password", placeholder="Re-enter your password")
521
+ submit = st.form_submit_button("Create Account", use_container_width=True, type="primary")
522
+
523
+ if submit:
524
+ if not username or not email or not password or not confirm_password:
525
+ st.error("All fields are required")
526
+ elif password != confirm_password:
527
+ st.error("Passwords do not match")
528
+ else:
529
+ success, msg = signup(username, email, password)
530
+ if success:
531
+ st.success(msg)
532
+ st.info("Please login with your credentials")
533
+ st.session_state.current_page = "login"
534
+ time.sleep(1)
535
+ st.rerun()
536
+ else:
537
+ st.error(msg)
538
+
539
+ st.divider()
540
+ if st.button("Already have an account? Login", use_container_width=True):
541
+ st.session_state.current_page = "login"
542
+ st.rerun()
543
+
544
+ def show_dashboard():
545
+ with st.sidebar:
546
+ st.markdown(f"""
547
+ <div style="padding: 0 0 1rem 0; text-align: center;">
548
+ <div style="font-size: 2.5rem; margin-bottom: 0.5rem;">🏦</div>
549
+ <h2 style="margin: 0; font-size: 1.3rem !important; font-family: 'Poppins', sans-serif;">Central Bank AI</h2>
550
+ </div>
551
+ """, unsafe_allow_html=True)
552
+
553
+ # User Info Section (New CSS Class)
554
+ st.markdown(f"""
555
+ <div class="user-card">
556
+ <div class="user-avatar">
557
+ {st.session_state.username[0].upper() if st.session_state.username else 'U'}
558
+ </div>
559
+ <div>
560
+ <div class="user-name">{st.session_state.username}</div>
561
+ <div class="user-email">{st.session_state.email}</div>
562
+ </div>
563
+ </div>
564
+ """, unsafe_allow_html=True)
565
+
566
+ if "current_tab" not in st.session_state:
567
+ st.session_state.current_tab = "Dashboard"
568
+
569
+ st.markdown("<div class='section-title'>Navigation</div>", unsafe_allow_html=True)
570
+
571
+ nav_btn_style1 = "primary" if st.session_state.current_tab == "Dashboard" else "secondary"
572
+ nav_btn_style2 = "primary" if st.session_state.current_tab == "Banking Assistant" else "secondary"
573
+
574
+ if st.button("📊 Dashboard", use_container_width=True, type=nav_btn_style1):
575
+ st.session_state.current_tab = "Dashboard"
576
+ st.rerun()
577
+
578
+ if st.button("� Banking Assistant", use_container_width=True, type=nav_btn_style2):
579
+ st.session_state.current_tab = "Banking Assistant"
580
+ st.rerun()
581
+
582
+ page = st.session_state.current_tab
583
+
584
+ st.markdown("<div style='margin-bottom: 24px;'></div>", unsafe_allow_html=True)
585
+
586
+ st.markdown("<div class='logout-btn'>", unsafe_allow_html=True)
587
+ if st.button("Logout", use_container_width=True):
588
+ logout()
589
+ st.rerun()
590
+ st.markdown("</div>", unsafe_allow_html=True)
591
+
592
+ # Push Chat History to the bottom
593
+ st.markdown("<div style='flex-grow: 1; min-height: 40px;'></div>", unsafe_allow_html=True)
594
+
595
+ st.markdown("<div class='section-title'>Recent Chats</div>", unsafe_allow_html=True)
596
+
597
+ new_col, clear_col = st.columns([1, 1])
598
+ with new_col:
599
+ if st.button("➕ New Chat", use_container_width=True):
600
+ st.session_state.messages = []
601
+ st.session_state.current_chat_id = None
602
+ st.rerun()
603
+ with clear_col:
604
+ if st.session_state.chat_sessions and st.button("🗑️ Clear All", use_container_width=True):
605
+ clear_all_chat_history(st.session_state.username, st.session_state)
606
+ st.session_state.messages = []
607
+ st.session_state.current_chat_id = None
608
+ st.rerun()
609
+
610
+ # Chat Sessions
611
+ st.markdown("<div class='chat-history-container'>", unsafe_allow_html=True)
612
+ if st.session_state.chat_sessions:
613
+ # Display only top 5 initially, or all if "show_all_chats" is True
614
+ if "show_all_chats" not in st.session_state:
615
+ st.session_state.show_all_chats = False
616
+
617
+ display_chats = st.session_state.chat_sessions if st.session_state.show_all_chats else st.session_state.chat_sessions[:5]
618
+
619
+ for chat in display_chats:
620
+ preview = chat.get('preview', 'No messages')
621
+ chat_id = chat['session_id']
622
+
623
+ chat_col1, chat_col2 = st.columns([4, 1])
624
+ with chat_col1:
625
+ if st.button(f"📄 {preview}", key=f"chat_{chat_id}", use_container_width=True):
626
+ st.session_state.messages = chat['messages']
627
+ st.session_state.current_chat_id = chat_id
628
+ st.rerun()
629
+ with chat_col2:
630
+ if st.button("❌", key=f"del_{chat_id}", use_container_width=True):
631
+ delete_chat_session(st.session_state.username, st.session_state, chat_id)
632
+ if st.session_state.current_chat_id == chat_id:
633
+ st.session_state.messages = []
634
+ st.session_state.current_chat_id = None
635
+ st.rerun()
636
+
637
+ # Show "See all" button if there are more than 5 chats
638
+ if len(st.session_state.chat_sessions) > 5:
639
+ if st.session_state.show_all_chats:
640
+ if st.button("See Less", use_container_width=True):
641
+ st.session_state.show_all_chats = False
642
+ st.rerun()
643
+ else:
644
+ if st.button(f"See All ({len(st.session_state.chat_sessions)})", use_container_width=True):
645
+ st.session_state.show_all_chats = True
646
+ st.rerun()
647
+ else:
648
+ st.caption("No recent chats")
649
+ st.markdown("</div>", unsafe_allow_html=True)
650
+
651
+ st.title("Dashboard" if page == "Dashboard" else "Banking Assistant")
652
+
653
+ if page == "Dashboard":
654
+ st.markdown("## 📊 Dashboard Overview")
655
+
656
+ # Custom Metric Cards
657
+ st.markdown(f"""
658
+ <div style="display: flex; gap: 20px; margin-bottom: 2rem;">
659
+ <div class="bank-card" style="flex: 1; text-align: center;">
660
+ <div style="color: {st.session_state.colors['text_secondary']}; font-size: 0.9rem; margin-bottom: 8px;">Account Balance</div>
661
+ <div style="font-size: 1.8rem; font-weight: 700;">{format_currency(st.session_state.balance)}</div>
662
+ </div>
663
+ <div class="bank-card" style="flex: 1; text-align: center;">
664
+ <div style="color: {st.session_state.colors['text_secondary']}; font-size: 0.9rem; margin-bottom: 8px;">Interest Rate</div>
665
+ <div style="font-size: 1.8rem; font-weight: 700;">{st.session_state.interest_rate}%</div>
666
+ </div>
667
+ <div class="bank-card" style="flex: 1; text-align: center;">
668
+ <div style="color: {st.session_state.colors['text_secondary']}; font-size: 0.9rem; margin-bottom: 8px;">Active Loans</div>
669
+ <div style="font-size: 1.8rem; font-size: 1.8rem; font-weight: 700;">{st.session_state.active_loans}</div>
670
+ </div>
671
+ </div>
672
+ """, unsafe_allow_html=True)
673
+
674
+ st.markdown("<div style='margin-bottom: 2rem;'></div>", unsafe_allow_html=True)
675
+
676
+ # 2 & 3. Insights & Health Score
677
+ col_health, col_insights = st.columns(2)
678
+ with col_health:
679
+ st.markdown(f"""
680
+ <div class="bank-card" style="height: 100%;">
681
+ <h3 style="margin-top:0;">🟢 Financial Health Score</h3>
682
+ <div style="font-size: 2.5rem; font-weight: 700; color: {st.session_state.colors['primary']};">78 <span style="font-size: 1rem; color: {st.session_state.colors['text_secondary']};">/ 100</span></div>
683
+ <div style="margin-top: 10px; font-size: 0.95rem; color: {st.session_state.colors['text_secondary']};">
684
+ <div style="margin-bottom: 4px;">✓ Good savings ratio</div>
685
+ <div style="margin-bottom: 4px;">✓ Low EMI burden</div>
686
+ <div>✓ Stable spending</div>
687
+ </div>
688
+ </div>
689
+ """, unsafe_allow_html=True)
690
+
691
+ with col_insights:
692
+ st.markdown(f"""
693
+ <div class="bank-card" style="height: 100%;">
694
+ <h3 style="margin-top:0;">💡 Smart Insights</h3>
695
+ <div style="margin-top: 15px; font-size: 0.95rem; line-height: 1.6;">
696
+ <div style="margin-bottom: 8px;">📈 This month your spending increased by <b>12%</b> compared to last month.</div>
697
+ <div style="margin-bottom: 8px;">🛍️ Most spending category: <b>Shopping</b>.</div>
698
+ <div>⚠️ EMI due in <b>5 days</b>.</div>
699
+ </div>
700
+ </div>
701
+ """, unsafe_allow_html=True)
702
+
703
+ st.markdown("<div style='margin-bottom: 1rem;'></div>", unsafe_allow_html=True)
704
+
705
+ # 4 & 5. Net Worth & Upcoming Dues
706
+ col_nw, col_dues = st.columns(2)
707
+ with col_nw:
708
+ st.markdown(f"""
709
+ <div class="bank-card" style="height: 100%;">
710
+ <h3 style="margin-top:0;">💎 Net Worth</h3>
711
+ <div style="display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 1rem;">
712
+ <span style="color: {st.session_state.colors['text_secondary']};">Assets (Savings + FD + Investments)</span>
713
+ <span style="font-weight: 600; color: {st.session_state.colors['success']};">{format_currency(st.session_state.balance + 3500000)}</span>
714
+ </div>
715
+ <div style="display: flex; justify-content: space-between; margin-bottom: 16px; border-bottom: 1px solid {st.session_state.colors['border']}; padding-bottom: 12px; font-size: 1rem;">
716
+ <span style="color: {st.session_state.colors['text_secondary']};">Liabilities (Loans + Credit Dues)</span>
717
+ <span style="font-weight: 600; color: {st.session_state.colors['danger']};">{format_currency(st.session_state.total_loan_amount)}</span>
718
+ </div>
719
+ <div style="display: flex; justify-content: space-between; align-items: center;">
720
+ <span style="font-weight: 600; font-size: 1.1rem;">Total Net Worth</span>
721
+ <span style="font-size: 1.5rem; font-weight: 700; color: {st.session_state.colors['primary']};">{format_currency(st.session_state.balance + 3500000 - st.session_state.total_loan_amount)}</span>
722
+ </div>
723
+ </div>
724
+ """, unsafe_allow_html=True)
725
+
726
+ with col_dues:
727
+ st.markdown(f"""
728
+ <div class="bank-card" style="height: 100%;">
729
+ <h3 style="margin-top:0;">📅 Upcoming Payments</h3>
730
+ <div style="margin-top: 15px; font-size: 1rem; line-height: 1.6;">
731
+ <div style="display: flex; justify-content: space-between; margin-bottom: 12px;">
732
+ <span>Home Loan EMI</span>
733
+ <div><span style="font-weight: 600;">₹12,000</span> <span style="font-size: 0.85rem; color: {st.session_state.colors['text_secondary']};">due 5 Mar</span></div>
734
+ </div>
735
+ <div style="display: flex; justify-content: space-between; margin-bottom: 12px;">
736
+ <span>Credit Card Bill</span>
737
+ <div><span style="font-weight: 600;">₹8,400</span> <span style="font-size: 0.85rem; color: {st.session_state.colors['text_secondary']};">due 9 Mar</span></div>
738
+ </div>
739
+ <div style="display: flex; justify-content: space-between;">
740
+ <span>Electricity Bill</span>
741
+ <div><span style="font-weight: 600;">₹1,500</span> <span style="font-size: 0.85rem; color: {st.session_state.colors['text_secondary']};">due 2 Mar</span></div>
742
+ </div>
743
+ </div>
744
+ </div>
745
+ """, unsafe_allow_html=True)
746
+
747
+ st.divider()
748
+
749
+ # Visualizations
750
+ col_left, col_right = st.columns([2, 1])
751
+ df = get_mock_transactions()
752
+
753
+ with col_left:
754
+ st.write("### 📉 Income vs Expenses")
755
+ daily_data = df.groupby(['Date', 'Type'])['Amount'].sum().reset_index()
756
+ fig_bar = px.bar(
757
+ daily_data,
758
+ x='Date',
759
+ y='Amount',
760
+ color='Type',
761
+ barmode='group',
762
+ color_discrete_map={"Income": st.session_state.colors['success'], "Expense": st.session_state.colors['danger']}
763
+ )
764
+ fig_bar.update_layout(margin=dict(t=0, b=0, l=0, r=0), height=300, paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", showlegend=True, legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1))
765
+ if st.session_state.theme == "dark":
766
+ fig_bar.update_layout(font_color="white")
767
+ else:
768
+ fig_bar.update_layout(font_color="black")
769
+ st.plotly_chart(fig_bar, use_container_width=True)
770
+
771
+ with col_right:
772
+ st.write("### 🍰 Expenses Breakdown")
773
+ expense_df = df[df['Type'] == 'Expense']
774
+ if expense_df.empty:
775
+ st.info("No expenses recorded yet.")
776
+ else:
777
+ category_data = expense_df.groupby('Category')['Amount'].sum().reset_index()
778
+ fig = px.pie(
779
+ category_data,
780
+ values='Amount',
781
+ names='Category',
782
+ hole=0.4,
783
+ color_discrete_sequence=[st.session_state.colors['primary'], st.session_state.colors['secondary'], '#38bdf8', '#818cf8', '#a78bfa', '#f472b6']
784
+ )
785
+ fig.update_layout(
786
+ margin=dict(t=0, b=0, l=0, r=0),
787
+ height=300,
788
+ showlegend=False
789
+ )
790
+ # Ensure transparent background for the chart
791
+ fig.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)")
792
+ if st.session_state.theme == "dark":
793
+ fig.update_layout(font_color="white")
794
+ else:
795
+ fig.update_layout(font_color="black")
796
+
797
+ st.plotly_chart(fig, use_container_width=True)
798
+
799
+ st.divider()
800
+
801
+ # Consolidated Transactions
802
+ st.markdown("### 📝 Recent Transaction History")
803
+ st.dataframe(
804
+ df.sort_values(by="Date", ascending=False),
805
+ use_container_width=True,
806
+ hide_index=True
807
+ )
808
+
809
+ else:
810
+ is_connected = check_ollama_connection()
811
+ col_h1, col_h2 = st.columns([4, 1])
812
+ with col_h2:
813
+ backend = get_active_backend()
814
+ if is_connected:
815
+ label = "☁️ Groq AI" if backend == "groq" else "🟢 Ollama"
816
+ st.markdown(f'<div class="status-badge status-online"><span>●</span> {label}</div>', unsafe_allow_html=True)
817
+ else:
818
+ st.markdown('<div class="status-badge status-offline"><span>○</span> Offline</div>', unsafe_allow_html=True)
819
+
820
+ st.markdown("<div style='margin-top: 1rem;'></div>", unsafe_allow_html=True)
821
+
822
+ # FAQ Suggestions
823
+ st.markdown("<div style='margin-bottom: 10px; font-size: 0.9rem; color: #64748b;'><strong>Popular Questions:</strong></div>", unsafe_allow_html=True)
824
+
825
+ faq_row1_col1, faq_row1_col2, faq_row1_col3 = st.columns(3)
826
+ with faq_row1_col1:
827
+ if st.button("💰 Balance?", use_container_width=True):
828
+ st.session_state.faq_trigger = "What is my balance?"
829
+ st.rerun()
830
+ with faq_row1_col2:
831
+ if st.button("📈 Interest?", use_container_width=True):
832
+ st.session_state.faq_trigger = "What are the current interest rates?"
833
+ st.rerun()
834
+ with faq_row1_col3:
835
+ if st.button("📞 Support", use_container_width=True):
836
+ st.session_state.faq_trigger = "How do I contact customer care?"
837
+ st.rerun()
838
+
839
+ faq_row2_col1, faq_row2_col2, faq_row2_col3 = st.columns(3)
840
+ with faq_row2_col1:
841
+ if st.button("🕒 Hours", use_container_width=True):
842
+ st.session_state.faq_trigger = "What are the working hours?"
843
+ st.rerun()
844
+ with faq_row2_col2:
845
+ if st.button("🏦 Min Bal", use_container_width=True):
846
+ st.session_state.faq_trigger = "What is the minimum balance?"
847
+ st.rerun()
848
+ with faq_row2_col3:
849
+ if st.button("📋 FD Rates", use_container_width=True):
850
+ st.session_state.faq_trigger = "What are the FD rates?"
851
+ st.rerun()
852
+
853
+ chat_container = st.container(height=400, border=False)
854
+
855
+ with chat_container:
856
+ for message in st.session_state.messages:
857
+ role = message["role"]
858
+ if role == "user":
859
+ st.markdown(f'<div class="user-bubble">{message["content"]}</div>', unsafe_allow_html=True)
860
+ else:
861
+ st.markdown(f'<div class="ai-bubble">{message["content"]}</div>', unsafe_allow_html=True)
862
+
863
+ prompt = st.chat_input("Ask about your finances or banking services...")
864
+
865
+ if getattr(st.session_state, 'faq_trigger', None):
866
+ prompt = st.session_state.faq_trigger
867
+ st.session_state.faq_trigger = None
868
+
869
+ if prompt:
870
+ st.session_state.messages.append({"role": "user", "content": prompt})
871
+
872
+ with chat_container:
873
+ st.markdown(f'<div class="user-bubble">{prompt}</div>', unsafe_allow_html=True)
874
+
875
+ faq_response = get_faq_response(prompt)
876
+
877
+ res_box = st.empty()
878
+ full_response = ""
879
+
880
+ if faq_response:
881
+ full_response = faq_response
882
+ res_box.markdown(f'<div class="ai-bubble">{full_response}</div>', unsafe_allow_html=True)
883
+ elif is_banking_query(prompt):
884
+ if check_ollama_connection():
885
+ last_update_time = time.time()
886
+ for chunk in stream_ai_response(prompt, history=st.session_state.messages[:-1]):
887
+ if chunk:
888
+ full_response += chunk
889
+ current_time = time.time()
890
+ if current_time - last_update_time > 0.05:
891
+ res_box.markdown(f'<div class="ai-bubble">{full_response}▌</div>', unsafe_allow_html=True)
892
+ last_update_time = current_time
893
+
894
+ res_box.markdown(f'<div class="ai-bubble">{full_response}</div>', unsafe_allow_html=True)
895
+ else:
896
+ full_response = "I'm having trouble reaching the AI engine right now. However, I can still help with basic queries like your balance or interest rates. How can I assist you?"
897
+ res_box.markdown(f'<div class="ai-bubble">{full_response}</div>', unsafe_allow_html=True)
898
+ else:
899
+ # Non-banking refusal
900
+ full_response = "I am a banking assistant and can only answer banking-related queries. Please feel free to ask about accounts, loans, cards, or other financial services."
901
+ res_box.markdown(f'<div class="ai-bubble">{full_response}</div>', unsafe_allow_html=True)
902
+
903
+ if not full_response:
904
+ full_response = "I'm having trouble reaching the main AI engine right now. However, I can still help with basic queries like your balance or interest rates. How can I assist you?"
905
+
906
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
907
+ # Save using the persistent utility
908
+ new_id = save_chat_session(st.session_state.username, st.session_state, st.session_state.messages, st.session_state.current_chat_id)
909
+ if not st.session_state.current_chat_id:
910
+ st.session_state.current_chat_id = new_id
911
+
912
+ st.rerun()
913
+
914
+ if not st.session_state.logged_in:
915
+ if st.session_state.current_page == "login":
916
+ show_login_page()
917
+ elif st.session_state.current_page == "signup":
918
+ show_signup_page()
919
+ else:
920
+ show_dashboard()
data/intents.json ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "intents": [
3
+ {
4
+ "tag": "greetings",
5
+ "patterns": [
6
+ "hi",
7
+ "hello",
8
+ "hey",
9
+ "good morning",
10
+ "good afternoon",
11
+ "good evening",
12
+ "is anyone there?",
13
+ "hello central bank ai"
14
+ ],
15
+ "responses": [
16
+ "Hello! Welcome to Central Bank AI. How can I assist you with your banking needs today?",
17
+ "Hi there! I'm your Central Bank virtual assistant. What can I do for you?"
18
+ ]
19
+ },
20
+ {
21
+ "tag": "goodbye",
22
+ "patterns": [
23
+ "bye",
24
+ "goodbye",
25
+ "see you later",
26
+ "thanks for the help",
27
+ "thank you",
28
+ "that's all"
29
+ ],
30
+ "responses": [
31
+ "Thank you for choosing Central Bank. Have a great day!",
32
+ "Goodbye! Feel free to reach out if you have more questions."
33
+ ]
34
+ },
35
+ {
36
+ "tag": "account_opening",
37
+ "patterns": [
38
+ "how to open account",
39
+ "create new bank account",
40
+ "open savings account",
41
+ "steps to open account",
42
+ "account opening procedure",
43
+ "documents for account",
44
+ "required documents",
45
+ "what documents do i need"
46
+ ],
47
+ "responses": [
48
+ "You can open a savings account online via our website or mobile app by providing your Aadhaar and PAN. Alternatively, visit any branch with your ID and address proof.",
49
+ "To open an account, you will need a valid ID (Aadhaar/Passport), PAN card, and proof of residence."
50
+ ]
51
+ },
52
+ {
53
+ "tag": "balance_enquiry",
54
+ "patterns": [
55
+ "balance",
56
+ "check balance",
57
+ "check my balance",
58
+ "what is my balance",
59
+ "account balance",
60
+ "how much money in my account",
61
+ "view balance"
62
+ ],
63
+ "responses": [
64
+ "Your current account balance is displayed on your dashboard. You can also check it via SMS banking or by calling our toll-free number.",
65
+ "To view your balance, simply log in to your dashboard or use our mobile banking app."
66
+ ]
67
+ },
68
+ {
69
+ "tag": "fund_transfer",
70
+ "patterns": [
71
+ "transfer money",
72
+ "send funds",
73
+ "how to transfer money",
74
+ "make a payment",
75
+ "wire transfer",
76
+ "neft rtgs imps"
77
+ ],
78
+ "responses": [
79
+ "You can transfer funds via NEFT, RTGS, or IMPS through the 'Transfer Money' section in your dashboard.",
80
+ "For instant transfers, use IMPS or UPI. For larger amounts, NEFT or RTGS are recommended."
81
+ ]
82
+ },
83
+ {
84
+ "tag": "debit_card",
85
+ "patterns": [
86
+ "debit card",
87
+ "apply for debit card",
88
+ "new debit card",
89
+ "block debit card",
90
+ "lost card"
91
+ ],
92
+ "responses": [
93
+ "You can manage your debit card, including blocking it or requesting a new one, under the 'Cards' section of your dashboard.",
94
+ "If your debit card is lost or stolen, please block it immediately via the app or call 1800-111-2222."
95
+ ]
96
+ },
97
+ {
98
+ "tag": "credit_card",
99
+ "patterns": [
100
+ "credit card",
101
+ "apply for credit card",
102
+ "credit card limit",
103
+ "pay credit card bill",
104
+ "credit card offers"
105
+ ],
106
+ "responses": [
107
+ "Apply for various credit cards through our 'Cards' portal. You can also view your statements and pay bills there.",
108
+ "Check your eligibility for a credit card increase in the 'Card Services' section."
109
+ ]
110
+ },
111
+ {
112
+ "tag": "atm_issues",
113
+ "patterns": [
114
+ "atm problem",
115
+ "money not dispensed",
116
+ "atm card stuck",
117
+ "atm pin reset",
118
+ "atm transaction failure"
119
+ ],
120
+ "responses": [
121
+ "If cash was not dispensed but debited, it will usually be reversed within 24-48 hours. If not, please lodge a complaint in the app.",
122
+ "You can reset your ATM PIN at any Central Bank ATM or via Internet Banking."
123
+ ]
124
+ },
125
+ {
126
+ "tag": "internet_banking",
127
+ "patterns": [
128
+ "internet banking",
129
+ "net banking",
130
+ "how to register for net banking",
131
+ "net banking login issues"
132
+ ],
133
+ "responses": [
134
+ "Register for Internet Banking online using your debit card and account details on our official website.",
135
+ "Ensure you never share your Internet Banking password or OTP with anyone."
136
+ ]
137
+ },
138
+ {
139
+ "tag": "mobile_banking",
140
+ "patterns": [
141
+ "mobile banking",
142
+ "banking app",
143
+ "how to use app",
144
+ "mobile app support"
145
+ ],
146
+ "responses": [
147
+ "Download our official mobile banking app from the Play Store or App Store for secure on-the-go banking.",
148
+ "Our mobile app allows you to manage accounts, pay bills, and transfer funds instantly."
149
+ ]
150
+ },
151
+ {
152
+ "tag": "loan_information",
153
+ "patterns": [
154
+ "loan",
155
+ "personal loan",
156
+ "home loan",
157
+ "car loan",
158
+ "loan eligibility",
159
+ "apply for loan"
160
+ ],
161
+ "responses": [
162
+ "We offer a range of loans including Home, Personal, and Education loans at competitive interest rates. Apply online via the 'Loans' tab.",
163
+ "Check your loan eligibility and required documents in the 'Loan Center' section of your dashboard."
164
+ ]
165
+ },
166
+ {
167
+ "tag": "emi_calculation",
168
+ "patterns": [
169
+ "emi",
170
+ "calculate emi",
171
+ "loan calculator",
172
+ "monthly installment"
173
+ ],
174
+ "responses": [
175
+ "Use our EMI Calculator on the website to estimate your monthly installments based on loan amount, interest, and tenure.",
176
+ "For a ₹10 lakh loan at 9% for 5 years, your approximate EMI would be around ₹20,758."
177
+ ]
178
+ },
179
+ {
180
+ "tag": "fixed_deposit",
181
+ "patterns": [
182
+ "fd",
183
+ "fixed deposit",
184
+ "fd interest rates",
185
+ "open fd",
186
+ "recurring deposit"
187
+ ],
188
+ "responses": [
189
+ "Open a Fixed Deposit (FD) instantly via Net Banking. Current interest rates are up to 7.5% per annum.",
190
+ "The minimum amount to open an FD is ₹5,000, and you can choose tenures from 7 days to 10 years."
191
+ ]
192
+ },
193
+ {
194
+ "tag": "kyc_update",
195
+ "patterns": [
196
+ "kyc",
197
+ "update kyc",
198
+ "kyc documents",
199
+ "re-kyc"
200
+ ],
201
+ "responses": [
202
+ "Update your KYC details by uploading self-attested documents like Aadhaar and PAN via our 'Profile' section.",
203
+ "KYC updates are mandatory every few years. You can complete it digitally without visiting the branch."
204
+ ]
205
+ },
206
+ {
207
+ "tag": "password_reset",
208
+ "patterns": [
209
+ "reset password",
210
+ "forgot password",
211
+ "change login password",
212
+ "password recovery"
213
+ ],
214
+ "responses": [
215
+ "Reset your password using the 'Forgot Password' link on the login page. You'll need your registered email/mobile.",
216
+ "For security, we recommend changing your banking password every 90 days."
217
+ ]
218
+ },
219
+ {
220
+ "tag": "upi_issues",
221
+ "patterns": [
222
+ "upi problem",
223
+ "upi payment failed",
224
+ "upi id",
225
+ "wrong upi transfer"
226
+ ],
227
+ "responses": [
228
+ "If a UPI transaction fails, the amount is usually refunded within 3-5 business days. Check the transaction status in your UPI app.",
229
+ "You can create or manage your UPI ID in the 'Payments' section of our mobile app."
230
+ ]
231
+ },
232
+ {
233
+ "tag": "transaction_failure",
234
+ "patterns": [
235
+ "transaction failed",
236
+ "payment declined",
237
+ "failed payment",
238
+ "money debited but not received"
239
+ ],
240
+ "responses": [
241
+ "Payment failures can occur due to network issues or insufficient funds. Check your 'Transaction History' for details.",
242
+ "If money was debited for a failed transaction, it will be automatically credited back within 48-72 hours."
243
+ ]
244
+ },
245
+ {
246
+ "tag": "charges_fees",
247
+ "patterns": [
248
+ "charges",
249
+ "bank fees",
250
+ "service charges",
251
+ "minimum balance penalty",
252
+ "transaction fees"
253
+ ],
254
+ "responses": [
255
+ "View our full schedule of charges on the website under 'Rates and Services'.",
256
+ "Maintain a minimum average balance of ₹10,000 to avoid non-maintenance charges."
257
+ ]
258
+ },
259
+ {
260
+ "tag": "cheque_book_request",
261
+ "patterns": [
262
+ "cheque book",
263
+ "order cheque book",
264
+ "stop cheque",
265
+ "cheque status"
266
+ ],
267
+ "responses": [
268
+ "You can request a new cheque book through the 'Services' tab. It will be delivered within 5 working days.",
269
+ "To stop a cheque payment, log in to Net Banking and go to the Cheque Services section."
270
+ ]
271
+ },
272
+ {
273
+ "tag": "branch_locator",
274
+ "patterns": [
275
+ "find branch",
276
+ "branch near me",
277
+ "ifsc code",
278
+ "bank address",
279
+ "branch timing"
280
+ ],
281
+ "responses": [
282
+ "Use the 'Branch Locator' on our website to find the nearest branch and its IFSC code.",
283
+ "Our branches are typically open from 9:30 AM to 4:30 PM on weekdays."
284
+ ]
285
+ },
286
+ {
287
+ "tag": "interest_rates",
288
+ "patterns": [
289
+ "interest",
290
+ "interest rates",
291
+ "current interest rates",
292
+ "savings interest",
293
+ "loan interest"
294
+ ],
295
+ "responses": [
296
+ "Our current savings account interest rate is 4.0% p.a., while Fixed Deposits offer up to 7.5% p.a. depending on the tenure.",
297
+ "Interest rates vary by product. Savings accounts currently offer 4.0%, and personal loans start at 10.5% p.a."
298
+ ]
299
+ },
300
+ {
301
+ "tag": "customer_support",
302
+ "patterns": [
303
+ "customer care",
304
+ "contact bank",
305
+ "helpline",
306
+ "support number",
307
+ "email support",
308
+ "call number",
309
+ "helpline number",
310
+ "support call no"
311
+ ],
312
+ "responses": [
313
+ "Reach our 24/7 customer support at 1800-444-5555 or email us at customercare@centralbank.com.",
314
+ "Our help desks at branches are available during business hours for in-person assistance."
315
+ ]
316
+ },
317
+ {
318
+ "tag": "fallback",
319
+ "patterns": [],
320
+ "responses": [
321
+ "I'm sorry, I didn't quite understand that. Could you please rephrase your banking-related question?",
322
+ "I'm not sure how to help with that. As a banking assistant, I can help you with accounts, loans, cards, and more. What would you like to know?"
323
+ ]
324
+ }
325
+ ]
326
+ }
ollama_integration.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import json
4
+ import time
5
+
6
+ # Auto-detect backend: Groq if API key is set, otherwise Ollama
7
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "")
8
+ USE_GROQ = bool(GROQ_API_KEY)
9
+
10
+ BANKING_KEYWORDS = [
11
+ "account", "loan", "card", "balance",
12
+ "transfer", "bank", "interest", "emi",
13
+ "credit", "debit", "kyc", "upi", "cheque",
14
+ "deposit", "fd", "rd", "branch", "ifsc",
15
+ "transaction", "payment", "savings", "checking",
16
+ "mortgage", "investment", "fintech", "wallet",
17
+ "rate", "rates", "support", "customer", "care",
18
+ "help", "contact", "helpline", "number", "call",
19
+ "document", "required", "identity", "proof", "open"
20
+ ]
21
+
22
+ SYSTEM_PROMPT = """You are BankBot, a professional banking assistant.
23
+ You ONLY answer banking-related questions.
24
+ If the question is not related to banking, politely refuse.
25
+ Never answer questions about politics, sports, entertainment, coding, or personal advice.
26
+ Please provide clear, professional, and helpful responses."""
27
+
28
+
29
+ def is_banking_query(user_input):
30
+ input_lower = user_input.lower()
31
+ return any(word in input_lower for word in BANKING_KEYWORDS)
32
+
33
+
34
+ def get_active_backend():
35
+ """Returns 'groq' if GROQ_API_KEY is set, otherwise 'ollama'."""
36
+ return "groq" if USE_GROQ else "ollama"
37
+
38
+
39
+ # ─── Groq AI Functions ────────────────────────────────────────────────────────
40
+
41
+ def get_groq_response(prompt, history=None, model="llama-3.3-70b-versatile"):
42
+ """Fetches a response from Groq AI API."""
43
+ try:
44
+ from groq import Groq
45
+ client = Groq(api_key=GROQ_API_KEY)
46
+
47
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
48
+
49
+ if history:
50
+ for msg in history[-5:]:
51
+ if msg.get("role") and msg.get("content"):
52
+ messages.append({"role": msg["role"], "content": msg["content"]})
53
+
54
+ messages.append({"role": "user", "content": prompt})
55
+
56
+ response = client.chat.completions.create(
57
+ model=model,
58
+ messages=messages,
59
+ temperature=0.1,
60
+ max_tokens=1000,
61
+ )
62
+ return response.choices[0].message.content
63
+ except Exception as e:
64
+ print(f"Groq Error: {e}")
65
+ return None
66
+
67
+
68
+ def stream_groq_response(prompt, history=None, model="llama-3.3-70b-versatile"):
69
+ """Yields streamed response chunks from Groq AI API."""
70
+ try:
71
+ from groq import Groq
72
+ client = Groq(api_key=GROQ_API_KEY)
73
+
74
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
75
+
76
+ if history:
77
+ for msg in history[-5:]:
78
+ if msg.get("role") and msg.get("content"):
79
+ messages.append({"role": msg["role"], "content": msg["content"]})
80
+
81
+ messages.append({"role": "user", "content": prompt})
82
+
83
+ stream = client.chat.completions.create(
84
+ model=model,
85
+ messages=messages,
86
+ temperature=0.1,
87
+ max_tokens=1000,
88
+ stream=True,
89
+ )
90
+ for chunk in stream:
91
+ content = chunk.choices[0].delta.content
92
+ if content:
93
+ yield content
94
+ except Exception as e:
95
+ print(f"Groq Stream Error: {e}")
96
+ yield None
97
+
98
+
99
+ # ─── Ollama Functions ─────────────────────────────────────────────────────────
100
+
101
+ def get_ollama_response(prompt, history=None, model="llama3:latest"):
102
+ """Fetches a response from a local Ollama instance."""
103
+ url = "http://127.0.0.1:11434/api/chat"
104
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
105
+
106
+ if history:
107
+ for msg in history[-5:]:
108
+ if msg.get("role") and msg.get("content"):
109
+ messages.append({"role": msg["role"], "content": msg["content"]})
110
+
111
+ messages.append({"role": "user", "content": prompt})
112
+
113
+ payload = {
114
+ "model": model,
115
+ "messages": messages,
116
+ "stream": False,
117
+ "options": {"temperature": 0.1, "top_p": 0.9, "num_predict": 1000}
118
+ }
119
+
120
+ try:
121
+ response = requests.post(url, json=payload, timeout=90)
122
+ response.raise_for_status()
123
+ data = response.json()
124
+ return data.get("message", {}).get("content", "")
125
+ except Exception as e:
126
+ print(f"Ollama Error: {e}")
127
+ if model == "llama3:latest":
128
+ return get_ollama_response(prompt, history, model="llama3")
129
+ return None
130
+
131
+
132
+ def stream_ollama_response(prompt, history=None, model="llama3:latest"):
133
+ """Yields chunks of the response from a local Ollama instance for streaming."""
134
+ url = "http://127.0.0.1:11434/api/chat"
135
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
136
+
137
+ if history:
138
+ for msg in history[-5:]:
139
+ if msg.get("role") and msg.get("content"):
140
+ messages.append({"role": msg["role"], "content": msg["content"]})
141
+
142
+ messages.append({"role": "user", "content": prompt})
143
+
144
+ payload = {
145
+ "model": model,
146
+ "messages": messages,
147
+ "stream": True,
148
+ "options": {"temperature": 0.1, "top_p": 0.9, "num_predict": 1000}
149
+ }
150
+
151
+ try:
152
+ response = requests.post(url, json=payload, timeout=90, stream=True)
153
+ response.raise_for_status()
154
+
155
+ for line in response.iter_lines():
156
+ if line:
157
+ chunk = json.loads(line)
158
+ if 'message' in chunk and 'content' in chunk['message']:
159
+ yield chunk['message']['content']
160
+ if chunk.get('done'):
161
+ break
162
+ except Exception as e:
163
+ print(f"Ollama Stream Error: {e}")
164
+ if model == "llama3:latest":
165
+ yield from stream_ollama_response(prompt, history, model="llama3")
166
+ else:
167
+ yield None
168
+
169
+
170
+ # ─── Unified Wrapper Functions ────────────────────────────────────────────────
171
+
172
+ def get_ai_response(prompt, history=None):
173
+ """Auto-selects Groq or Ollama based on environment."""
174
+ if USE_GROQ:
175
+ return get_groq_response(prompt, history)
176
+ return get_ollama_response(prompt, history)
177
+
178
+
179
+ def stream_ai_response(prompt, history=None):
180
+ """Auto-selects streaming from Groq or Ollama based on environment."""
181
+ if USE_GROQ:
182
+ yield from stream_groq_response(prompt, history)
183
+ else:
184
+ yield from stream_ollama_response(prompt, history)
185
+
186
+
187
+ def check_ollama_connection():
188
+ """Checks if the local Ollama server is running."""
189
+ if USE_GROQ:
190
+ return True
191
+ try:
192
+ response = requests.get("http://127.0.0.1:11434/", timeout=2)
193
+ return response.status_code == 200
194
+ except:
195
+ return False
196
+
197
+
198
+ def rewrite_banking_response(predefined_answer):
199
+ """Uses the active AI backend to rewrite a predefined FAQ response."""
200
+ prompt = f"Rewrite this banking answer to be complete and detailed according to all formal rules:\n\n{predefined_answer}"
201
+ return get_ai_response(prompt)
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
users.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"admin": {"email": "admin@gmail.com", "password": "Admin@123"}, "mohsin": {"email": "mohsin@gmail.com", "password": "Mohsin@123"}}
utils.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import requests
3
+ from datetime import datetime
4
+ import uuid
5
+ import json
6
+ import os
7
+ import random
8
+ import streamlit as st
9
+ from ollama_integration import (
10
+ get_ollama_response,
11
+ stream_ollama_response,
12
+ get_ai_response,
13
+ stream_ai_response,
14
+ get_active_backend,
15
+ rewrite_banking_response,
16
+ is_banking_query
17
+ )
18
+
19
+ USER_FILE = "users.json"
20
+ SESSION_FILE = "session.json"
21
+ HISTORY_FILE = "chat_history.json"
22
+ INTENTS_FILE = os.path.join("data", "intents.json")
23
+
24
+ @st.cache_data
25
+ def load_intents():
26
+ if not os.path.exists(INTENTS_FILE):
27
+ return {"intents": []}
28
+ try:
29
+ with open(INTENTS_FILE, "r") as f:
30
+ return json.load(f)
31
+ except Exception as e:
32
+ print(f"Error loading intents: {e}")
33
+ return {"intents": []}
34
+
35
+ # Global intents data, initialized from cached function
36
+ intents_data = load_intents()
37
+
38
+ def persist_user(username, email, password):
39
+ users = get_persisted_users()
40
+ users[username] = {"email": email, "password": password}
41
+ with open(USER_FILE, "w") as f:
42
+ json.dump(users, f)
43
+
44
+ def get_persisted_users():
45
+ if not os.path.exists(USER_FILE):
46
+ return {}
47
+ try:
48
+ with open(USER_FILE, "r") as f:
49
+ return json.load(f)
50
+ except:
51
+ return {}
52
+
53
+ def save_active_session(username):
54
+ with open(SESSION_FILE, "w") as f:
55
+ json.dump({"username": username}, f)
56
+
57
+ def get_active_session():
58
+ if not os.path.exists(SESSION_FILE):
59
+ return None
60
+ try:
61
+ with open(SESSION_FILE, "r") as f:
62
+ data = json.load(f)
63
+ return data.get("username")
64
+ except:
65
+ return None
66
+
67
+ def clear_active_session():
68
+ if os.path.exists(SESSION_FILE):
69
+ os.remove(SESSION_FILE)
70
+
71
+ def validate_email(email):
72
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
73
+ return re.match(pattern, email) is not None
74
+
75
+ def validate_password_strength(password):
76
+ if len(password) < 8:
77
+ return False, "Password must be at least 8 characters long"
78
+
79
+ if not re.search(r'[A-Z]', password):
80
+ return False, "Password must contain at least one uppercase letter"
81
+
82
+ if not re.search(r'[a-z]', password):
83
+ return False, "Password must contain at least one lowercase letter"
84
+
85
+ if not re.search(r'\d', password):
86
+ return False, "Password must contain at least one number"
87
+
88
+ if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
89
+ return False, "Password must contain at least one special character"
90
+
91
+ return True, "Password is strong"
92
+
93
+ def format_currency(amount):
94
+ return f"₹{amount:,.2f}"
95
+
96
+ def get_timestamp():
97
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
98
+
99
+ def generate_session_id():
100
+ return str(uuid.uuid4())
101
+
102
+ def get_chat_preview(messages, max_length=50):
103
+ if not messages:
104
+ return "Empty chat"
105
+
106
+ for msg in messages:
107
+ if msg["role"] == "user":
108
+ content = msg["content"]
109
+ if len(content) > max_length:
110
+ return content[:max_length] + "..."
111
+ return content
112
+
113
+ return "No user messages"
114
+
115
+ @st.cache_data(ttl=30)
116
+ def load_history_file():
117
+ if not os.path.exists(HISTORY_FILE):
118
+ return {}
119
+ try:
120
+ with open(HISTORY_FILE, "r") as f:
121
+ return json.load(f)
122
+ except:
123
+ return {}
124
+
125
+ def save_history_file(history):
126
+ with open(HISTORY_FILE, "w") as f:
127
+ json.dump(history, f, indent=4)
128
+
129
+ def get_all_chat_sessions(username):
130
+ history = load_history_file()
131
+ return history.get(username, [])
132
+
133
+ def save_chat_session(username, session_state, messages, session_id=None):
134
+ if not messages or len(messages) == 0:
135
+ return None
136
+
137
+ history = load_history_file()
138
+ user_sessions = history.get(username, [])
139
+
140
+ if session_id:
141
+ # Update existing session
142
+ found = False
143
+ for session in user_sessions:
144
+ if session["session_id"] == session_id:
145
+ session["messages"] = messages
146
+ session["preview"] = get_chat_preview(messages)
147
+ session["timestamp"] = get_timestamp()
148
+ found = True
149
+ break
150
+
151
+ # Also update in-memory session_state for immediate UI feedback
152
+ for session in session_state.chat_sessions:
153
+ if session["session_id"] == session_id:
154
+ session["messages"] = messages
155
+ session["preview"] = get_chat_preview(messages)
156
+ session["timestamp"] = get_timestamp()
157
+ break
158
+ else:
159
+ # Create new session
160
+ session_id = generate_session_id()
161
+ new_session = {
162
+ "session_id": session_id,
163
+ "timestamp": get_timestamp(),
164
+ "messages": messages,
165
+ "preview": get_chat_preview(messages)
166
+ }
167
+ user_sessions.insert(0, new_session)
168
+
169
+ if "chat_sessions" not in session_state:
170
+ session_state.chat_sessions = []
171
+ session_state.chat_sessions.insert(0, new_session)
172
+
173
+ history[username] = user_sessions
174
+ save_history_file(history)
175
+ return session_id
176
+
177
+ def load_chat_session(username, session_id):
178
+ user_sessions = get_all_chat_sessions(username)
179
+ for session in user_sessions:
180
+ if session["session_id"] == session_id:
181
+ return session["messages"]
182
+ return None
183
+
184
+ def delete_chat_session(username, session_state, session_id):
185
+ history = load_history_file()
186
+ user_sessions = history.get(username, [])
187
+
188
+ user_sessions = [s for s in user_sessions if s["session_id"] != session_id]
189
+ history[username] = user_sessions
190
+ save_history_file(history)
191
+
192
+ if "chat_sessions" in session_state:
193
+ session_state.chat_sessions = [s for s in session_state.chat_sessions if s["session_id"] != session_id]
194
+ return True
195
+
196
+ def clear_all_chat_history(username, session_state):
197
+ history = load_history_file()
198
+ history[username] = []
199
+ save_history_file(history)
200
+
201
+ session_state.chat_sessions = []
202
+ return True
203
+
204
+ @st.cache_data(ttl=10)
205
+ def check_ollama_connection():
206
+ from ollama_integration import check_ollama_connection as _check
207
+ return _check()
208
+
209
+ def get_faq_response(prompt):
210
+ """
211
+ Checks if the user's prompt matches any common frequently asked questions
212
+ using the structured intents.json data.
213
+ """
214
+ prompt_lower = prompt.lower().strip()
215
+
216
+ if not intents_data or "intents" not in intents_data:
217
+ return None
218
+
219
+ # Iterate through intents to find a matching pattern
220
+ for intent in intents_data["intents"]:
221
+ for pattern in intent["patterns"]:
222
+ p_lower = pattern.lower()
223
+ # For short patterns (like 'hi'), use word boundary check
224
+ if len(p_lower) <= 3:
225
+ if re.search(rf"\b{re.escape(p_lower)}\b", prompt_lower):
226
+ return random.choice(intent["responses"])
227
+ # For longer patterns, substring match is usually fine and more flexible
228
+ elif p_lower in prompt_lower:
229
+ return random.choice(intent["responses"])
230
+
231
+ return None