HassanJalil commited on
Commit
aff85e8
·
verified ·
1 Parent(s): 51a41f8

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +1248 -0
app.py ADDED
@@ -0,0 +1,1248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import google.generativeai as genai
3
+ from PyPDF2 import PdfReader
4
+ import os
5
+ import re
6
+ import json
7
+ import pickle
8
+ import hashlib
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
12
+ from langchain_huggingface.embeddings import HuggingFaceEmbeddings
13
+ from langchain_community.vectorstores import FAISS
14
+ from langchain.schema import Document
15
+ import tempfile
16
+ import warnings
17
+ import numpy as np
18
+ import shutil
19
+ import time
20
+ warnings.filterwarnings('ignore')
21
+
22
+ # Configure page
23
+ st.set_page_config(
24
+ page_title="Ashok 2.0 - AI Problem Solving Assistant",
25
+ page_icon="🧠",
26
+ layout="centered",
27
+ initial_sidebar_state="collapsed"
28
+ )
29
+
30
+ # World-class minimal UI styling
31
+ st.markdown("""
32
+ <style>
33
+ /* Import premium font */
34
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
35
+
36
+ /* Global Reset & Base */
37
+ .stApp {
38
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
39
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
40
+ }
41
+
42
+ .main .block-container {
43
+ padding-top: 2rem;
44
+ padding-bottom: 2rem;
45
+ max-width: 800px;
46
+ }
47
+
48
+ /* Hide Streamlit UI elements */
49
+ #MainMenu {visibility: hidden;}
50
+ footer {visibility: hidden;}
51
+ header {visibility: hidden;}
52
+ .stDeployButton {display: none;}
53
+
54
+ /* Main Heading */
55
+ .main-title {
56
+ font-size: 4rem;
57
+ font-weight: 700;
58
+ text-align: center;
59
+ color: #1f2937; /* Professional blue */
60
+ margin: 2rem 0 3rem 0;
61
+ letter-spacing: -0.02em;
62
+ line-height: 1.1;
63
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
64
+ }
65
+
66
+ /* API Key Setup Container */
67
+ .api-setup-container {
68
+ background: rgba(255, 255, 255, 0.9);
69
+ backdrop-filter: blur(10px);
70
+ border: 1px solid rgba(229, 231, 235, 0.6);
71
+ border-radius: 24px;
72
+ padding: 3rem;
73
+ margin: 2rem auto;
74
+ max-width: 500px;
75
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.05), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
76
+ text-align: center;
77
+ }
78
+
79
+ .api-setup-title {
80
+ font-size: 1.5rem;
81
+ font-weight: 600;
82
+ color: #1f2937;
83
+ margin-bottom: 1rem;
84
+ }
85
+
86
+ .api-setup-subtitle {
87
+ color: #6b7280;
88
+ margin-bottom: 2rem;
89
+ line-height: 1.6;
90
+ }
91
+
92
+ /* API Key Input Styling - Fixed */
93
+ .stTextInput > div > div > input {
94
+ background: rgba(255, 255, 255, 0.9);
95
+ border: 2px solid rgba(229, 231, 235, 0.6);
96
+ border-radius: 16px;
97
+ padding: 1rem 1.5rem;
98
+ font-size: 1rem;
99
+ transition: all 0.3s ease;
100
+ backdrop-filter: blur(5px);
101
+ color: #1f2937 !important;
102
+ }
103
+
104
+ .stTextInput > div > div > input::placeholder {
105
+ color: #9ca3af !important;
106
+ opacity: 1;
107
+ }
108
+
109
+ .stTextInput > div > div > input:focus {
110
+ border-color: #3b82f6;
111
+ box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
112
+ outline: none;
113
+ background: rgba(255, 255, 255, 0.95);
114
+ color: #1f2937 !important;
115
+ }
116
+
117
+ .stTextInput label {
118
+ font-weight: 500;
119
+ color: #374151 !important;
120
+ margin-bottom: 0.5rem;
121
+ }
122
+
123
+ /* Chat Interface */
124
+ .chat-container {
125
+ background: rgba(255, 255, 255, 0.7);
126
+ backdrop-filter: blur(10px);
127
+ border: 1px solid rgba(229, 231, 235, 0.4);
128
+ border-radius: 24px;
129
+ padding: 2rem;
130
+ margin: 2rem 0;
131
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.03), 0 4px 6px -2px rgba(0, 0, 0, 0.02);
132
+ }
133
+
134
+ /* Chat Messages */
135
+ .stChatMessage {
136
+ background: rgba(255, 255, 255, 0.9) !important;
137
+ border: 1px solid rgba(0, 0, 0, 0.08) !important;
138
+ border-radius: 16px !important;
139
+ margin-bottom: 1rem !important;
140
+ padding: 1.5rem !important;
141
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02) !important;
142
+ backdrop-filter: blur(5px) !important;
143
+ }
144
+
145
+ .stChatMessage[data-testid="chat-message-user"] {
146
+ background: rgba(248, 250, 252, 0.9) !important;
147
+ border-left: 3px solid #3b82f6 !important;
148
+ }
149
+
150
+ .stChatMessage[data-testid="chat-message-assistant"] {
151
+ background: rgba(255, 255, 255, 0.9) !important;
152
+ border-left: 3px solid #10b981 !important;
153
+ }
154
+
155
+ .stChatMessage .stMarkdown {
156
+ color: #1f2937 !important;
157
+ line-height: 1.6;
158
+ }
159
+
160
+ .stChatMessage .stMarkdown p {
161
+ color: #1f2937 !important;
162
+ margin-bottom: 0.5rem;
163
+ }
164
+
165
+ /* Chat Input */
166
+ .stChatInput > div {
167
+ background: rgba(255, 255, 255, 0.9);
168
+ border: 1px solid rgba(229, 231, 235, 0.6);
169
+ border-radius: 20px;
170
+ backdrop-filter: blur(10px);
171
+ }
172
+
173
+ .stChatInput input {
174
+ color: #1f2937 !important;
175
+ background: transparent !important;
176
+ border: none !important;
177
+ padding: 1rem 1.5rem !important;
178
+ }
179
+
180
+ .stChatInput input::placeholder {
181
+ color: #ffffff !important;
182
+ }
183
+
184
+ /* Quick Actions */
185
+ .quick-actions-container {
186
+ background: rgba(255, 255, 255, 0.6);
187
+ backdrop-filter: blur(10px);
188
+ border: 1px solid rgba(229, 231, 235, 0.4);
189
+ border-radius: 20px;
190
+ padding: 2rem;
191
+ margin: 2rem 0;
192
+ }
193
+
194
+ .quick-actions-title {
195
+ font-size: 1.25rem;
196
+ font-weight: 600;
197
+ color: #1f2937;
198
+ text-align: center;
199
+ margin-bottom: 1.5rem;
200
+ }
201
+
202
+ .quick-action-grid {
203
+ display: grid;
204
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
205
+ gap: 1rem;
206
+ margin-top: 1rem;
207
+ }
208
+
209
+ .quick-action-item {
210
+ background: rgba(255, 255, 255, 0.8);
211
+ border: 1px solid rgba(229, 231, 235, 0.5);
212
+ border-radius: 16px;
213
+ padding: 1.5rem;
214
+ cursor: pointer;
215
+ transition: all 0.3s ease;
216
+ text-align: left;
217
+ backdrop-filter: blur(5px);
218
+ }
219
+
220
+ .quick-action-item:hover {
221
+ background: rgba(248, 250, 252, 0.9);
222
+ border-color: #3b82f6;
223
+ transform: translateY(-2px);
224
+ box-shadow: 0 8px 25px rgba(59, 130, 246, 0.1);
225
+ }
226
+
227
+ .quick-action-icon {
228
+ font-size: 1.5rem;
229
+ margin-bottom: 0.5rem;
230
+ }
231
+
232
+ .quick-action-title {
233
+ font-weight: 600;
234
+ color: #1f2937;
235
+ margin-bottom: 0.5rem;
236
+ font-size: 1rem;
237
+ }
238
+
239
+ .quick-action-desc {
240
+ color: #6b7280;
241
+ font-size: 0.9rem;
242
+ line-height: 1.5;
243
+ }
244
+
245
+ /* Control Panel */
246
+ .control-panel {
247
+ display: flex;
248
+ justify-content: center;
249
+ gap: 1rem;
250
+ margin: 2rem 0;
251
+ }
252
+
253
+ .control-button {
254
+ background: rgba(255, 255, 255, 0.8);
255
+ border: 1px solid rgba(229, 231, 235, 0.6);
256
+ border-radius: 12px;
257
+ padding: 0.75rem 1.5rem;
258
+ color: #374151;
259
+ font-weight: 500;
260
+ cursor: pointer;
261
+ transition: all 0.3s ease;
262
+ backdrop-filter: blur(5px);
263
+ }
264
+
265
+ .control-button:hover {
266
+ background: rgba(248, 250, 252, 0.9);
267
+ border-color: #3b82f6;
268
+ color: #1f2937;
269
+ transform: translateY(-1px);
270
+ }
271
+
272
+ /* Status Indicators */
273
+ .status-success {
274
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%);
275
+ border: 1px solid rgba(16, 185, 129, 0.2);
276
+ border-radius: 16px;
277
+ padding: 1rem 1.5rem;
278
+ margin: 1rem 0;
279
+ color: #065f46;
280
+ font-weight: 500;
281
+ text-align: center;
282
+ backdrop-filter: blur(5px);
283
+ }
284
+
285
+ .status-warning {
286
+ background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%);
287
+ border: 1px solid rgba(245, 158, 11, 0.2);
288
+ border-radius: 16px;
289
+ padding: 1rem 1.5rem;
290
+ margin: 1rem 0;
291
+ color: #92400e;
292
+ font-weight: 500;
293
+ text-align: center;
294
+ backdrop-filter: blur(5px);
295
+ }
296
+
297
+ /* Learning Indicator */
298
+ .learning-indicator {
299
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.9) 0%, rgba(5, 150, 105, 0.9) 100%);
300
+ color: white;
301
+ padding: 1rem 1.5rem;
302
+ border-radius: 16px;
303
+ margin: 1rem 0;
304
+ font-weight: 500;
305
+ text-align: center;
306
+ box-shadow: 0 4px 6px rgba(16, 185, 129, 0.2);
307
+ backdrop-filter: blur(10px);
308
+ }
309
+
310
+ /* Typing Indicator */
311
+ .typing-indicator {
312
+ background: rgba(248, 250, 252, 0.9);
313
+ border: 1px solid rgba(229, 231, 235, 0.6);
314
+ border-radius: 16px;
315
+ padding: 1rem 1.5rem;
316
+ margin: 1rem 0;
317
+ display: flex;
318
+ align-items: center;
319
+ gap: 0.75rem;
320
+ color: #6b7280;
321
+ backdrop-filter: blur(5px);
322
+ }
323
+
324
+ .typing-dots {
325
+ display: flex;
326
+ gap: 4px;
327
+ }
328
+
329
+ .typing-dot {
330
+ width: 6px;
331
+ height: 6px;
332
+ border-radius: 50%;
333
+ background-color: #9ca3af;
334
+ animation: typing 1.4s infinite ease-in-out;
335
+ }
336
+
337
+ .typing-dot:nth-child(1) { animation-delay: -0.32s; }
338
+ .typing-dot:nth-child(2) { animation-delay: -0.16s; }
339
+
340
+ @keyframes typing {
341
+ 0%, 80%, 100% { transform: scale(0.8); opacity: 0.4; }
342
+ 40% { transform: scale(1); opacity: 1; }
343
+ }
344
+
345
+ /* Button Styling */
346
+ .stButton > button {
347
+ background: linear-gradient(135deg, rgba(59, 130, 246, 0.9) 0%, rgba(37, 99, 235, 0.9) 100%);
348
+ color: white !important;
349
+ border: none;
350
+ border-radius: 12px;
351
+ padding: 0.75rem 2rem;
352
+ font-weight: 500;
353
+ transition: all 0.3s ease;
354
+ backdrop-filter: blur(10px);
355
+ box-shadow: 0 4px 6px rgba(59, 130, 246, 0.2);
356
+ }
357
+
358
+ .stButton > button:hover {
359
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.9) 0%, rgba(29, 78, 216, 0.9) 100%);
360
+ transform: translateY(-1px);
361
+ box-shadow: 0 6px 12px rgba(59, 130, 246, 0.25);
362
+ }
363
+
364
+ /* API Key Guide */
365
+ .api-guide {
366
+ background: rgba(255, 255, 255, 0.6);
367
+ border: 1px solid rgba(229, 231, 235, 0.4);
368
+ border-radius: 16px;
369
+ padding: 1.5rem;
370
+ margin: 1.5rem 0;
371
+ backdrop-filter: blur(5px);
372
+ }
373
+
374
+ .api-guide-title {
375
+ font-weight: 600;
376
+ color: #1f2937;
377
+ margin-bottom: 1rem;
378
+ }
379
+
380
+ .api-guide-text {
381
+ color: #6b7280;
382
+ line-height: 1.6;
383
+ margin-bottom: 0.5rem;
384
+ }
385
+
386
+ .api-link {
387
+ color: #3b82f6;
388
+ text-decoration: none;
389
+ font-weight: 500;
390
+ transition: color 0.2s ease;
391
+ }
392
+
393
+ .api-link:hover {
394
+ color: #1d4ed8;
395
+ }
396
+
397
+ /* Responsive Design */
398
+ @media (max-width: 768px) {
399
+ .main-title {
400
+ font-size: 3rem;
401
+ }
402
+
403
+ .api-setup-container {
404
+ padding: 2rem;
405
+ margin: 1rem;
406
+ }
407
+
408
+ .quick-action-grid {
409
+ grid-template-columns: 1fr;
410
+ }
411
+
412
+ .control-panel {
413
+ flex-direction: column;
414
+ align-items: center;
415
+ }
416
+ }
417
+ .stChatFloatingInputContainer{
418
+ background-color: transparent !important;
419
+ }
420
+ </style>
421
+ """, unsafe_allow_html=True)
422
+
423
+ class PersistentHFKnowledgeBase:
424
+ """Persistent Knowledge Base with silent initialization"""
425
+
426
+ def __init__(self):
427
+ # Create persistent directories
428
+ self.data_dir = Path("./persistent_data")
429
+ self.data_dir.mkdir(exist_ok=True)
430
+
431
+ # File paths for persistence
432
+ self.vectorstore_path = self.data_dir / "vectorstore"
433
+ self.metadata_path = self.data_dir / "metadata.json"
434
+ self.conversations_path = self.data_dir / "conversations.json"
435
+ self.stats_path = self.data_dir / "stats.json"
436
+ self.init_flag_path = self.data_dir / "initialized.flag"
437
+
438
+ # Initialize components
439
+ self.embeddings = None
440
+ self.vectorstore = None
441
+ self.metadata = {}
442
+ self.conversations = []
443
+ self.stats = {}
444
+
445
+ # Initialize system silently
446
+ self.initialize_system()
447
+
448
+ def initialize_system(self):
449
+ """Initialize the complete system with silent book processing"""
450
+ try:
451
+ # Initialize embeddings first
452
+ self.init_embeddings()
453
+
454
+ # Check if system was already initialized
455
+ if self.init_flag_path.exists():
456
+ # Load existing knowledge base silently
457
+ self.load_existing_knowledge()
458
+ else:
459
+ # First time initialization - do it silently
460
+ self.first_time_initialization()
461
+
462
+ except Exception as e:
463
+ # Silent fallback initialization
464
+ self.fallback_initialization()
465
+
466
+ def init_embeddings(self):
467
+ """Initialize embeddings model with silent caching"""
468
+ if self.embeddings is None:
469
+ try:
470
+ cache_dir = self.data_dir / "embeddings_cache"
471
+ cache_dir.mkdir(exist_ok=True)
472
+
473
+ self.embeddings = HuggingFaceEmbeddings(
474
+ model_name="sentence-transformers/all-MiniLM-L6-v2",
475
+ cache_folder=str(cache_dir)
476
+ )
477
+ except Exception as e:
478
+ return False
479
+ return True
480
+
481
+ def first_time_initialization(self):
482
+ """Complete first-time setup with silent book processing"""
483
+ try:
484
+ # Initialize metadata
485
+ self.metadata = {
486
+ 'version': '2.0-minimal',
487
+ 'created_at': datetime.now().isoformat(),
488
+ 'last_updated': datetime.now().isoformat(),
489
+ 'total_documents': 0,
490
+ 'book_processed': False,
491
+ 'book_info': {},
492
+ 'initialization_complete': False
493
+ }
494
+
495
+ # Initialize stats
496
+ self.stats = {
497
+ 'total_queries': 0,
498
+ 'learning_sessions': 0,
499
+ 'book_chunks': 0,
500
+ 'conversation_chunks': 0,
501
+ 'silly_questions_blocked': 0
502
+ }
503
+
504
+ # Initialize conversations
505
+ self.conversations = []
506
+
507
+ # Process book if available (silently)
508
+ book_processed = self.process_startup_book()
509
+
510
+ # Create default knowledge if no book
511
+ if not book_processed:
512
+ self.create_default_knowledge()
513
+
514
+ # Mark as initialized
515
+ self.metadata['initialization_complete'] = True
516
+ self.save_all_data()
517
+
518
+ # Create initialization flag
519
+ with open(self.init_flag_path, 'w') as f:
520
+ f.write(f"Initialized on {datetime.now().isoformat()}")
521
+
522
+ except Exception as e:
523
+ self.fallback_initialization()
524
+
525
+ def process_startup_book(self):
526
+ """Process the book included with the deployment (silently)"""
527
+ book_paths = [
528
+ "book.pdf",
529
+ "problem_solving_book.pdf",
530
+ "default_book.pdf",
531
+ "ashok_book.pdf"
532
+ ]
533
+
534
+ for book_path in book_paths:
535
+ if Path(book_path).exists():
536
+ success = self.process_book_file(Path(book_path))
537
+ if success:
538
+ return True
539
+
540
+ return False
541
+
542
+ def process_book_file(self, book_path):
543
+ """Process a specific book file (silently)"""
544
+ try:
545
+ # Extract text from PDF
546
+ reader = PdfReader(str(book_path))
547
+ page_texts = []
548
+
549
+ for page_num, page in enumerate(reader.pages, 1):
550
+ page_text = page.extract_text()
551
+ if page_text.strip():
552
+ page_texts.append({
553
+ 'page': page_num,
554
+ 'text': page_text,
555
+ 'word_count': len(page_text.split())
556
+ })
557
+
558
+ if not page_texts:
559
+ return False
560
+
561
+ # Create book info
562
+ book_info = {
563
+ 'title': book_path.name,
564
+ 'path': str(book_path),
565
+ 'pages': len(page_texts),
566
+ 'processed_at': datetime.now().isoformat(),
567
+ 'source': 'deployment_book'
568
+ }
569
+
570
+ # Process content
571
+ success, message = self.process_book_content("", page_texts, book_info)
572
+
573
+ return success
574
+
575
+ except Exception as e:
576
+ return False
577
+
578
+ def create_default_knowledge(self):
579
+ """Create comprehensive default knowledge base"""
580
+ default_knowledge = [
581
+ {
582
+ "content": "Problem-solving methodology: 1) Problem Definition - Clearly articulate what needs to be solved, 2) Information Gathering - Collect relevant data and context, 3) Root Cause Analysis - Identify underlying causes, not just symptoms, 4) Solution Generation - Brainstorm multiple potential solutions, 5) Solution Evaluation - Assess feasibility, impact, and resources, 6) Implementation Planning - Create detailed action steps, 7) Execution and Monitoring - Implement and track progress, 8) Review and Learning - Evaluate outcomes and extract lessons.",
583
+ "metadata": {
584
+ "source": "core_knowledge",
585
+ "type": "framework",
586
+ "topic": "problem_solving_process",
587
+ "chapter": "Core Problem-Solving Framework"
588
+ }
589
+ },
590
+ {
591
+ "content": "Decision-making best practices: Use the DECIDE framework - D: Define the problem clearly, E: Establish criteria for solutions, C: Consider alternatives systematically, I: Identify best alternatives using criteria, D: Develop and implement action plan, E: Evaluate and monitor solution effectiveness. Always consider stakeholder impact, resource constraints, time limitations, and potential risks.",
592
+ "metadata": {
593
+ "source": "core_knowledge",
594
+ "type": "framework",
595
+ "topic": "decision_making",
596
+ "chapter": "Decision-Making Framework"
597
+ }
598
+ },
599
+ {
600
+ "content": "Conflict resolution strategies: 1) Active Listening - Understand all perspectives without judgment, 2) Identify Interests - Focus on underlying needs, not stated positions, 3) Find Common Ground - Identify shared goals and values, 4) Generate Options - Create win-win solutions collaboratively, 5) Use Objective Criteria - Apply fair standards for evaluation, 6) Separate People from Problems - Address issues, not personalities, 7) Maintain Relationships - Preserve working relationships while solving problems.",
601
+ "metadata": {
602
+ "source": "core_knowledge",
603
+ "type": "strategy",
604
+ "topic": "conflict_resolution",
605
+ "chapter": "Conflict Resolution Techniques"
606
+ }
607
+ },
608
+ {
609
+ "content": "Critical thinking skills development: Analysis (breaking complex information into components), Evaluation (assessing credibility and logical strength), Inference (drawing reasonable conclusions), Interpretation (understanding meaning and significance), Explanation (articulating reasoning clearly), Self-regulation (monitoring and correcting one's thinking). Practice questioning assumptions, considering multiple perspectives, examining evidence quality, and recognizing logical fallacies.",
610
+ "metadata": {
611
+ "source": "core_knowledge",
612
+ "type": "skills",
613
+ "topic": "critical_thinking",
614
+ "chapter": "Critical Thinking Development"
615
+ }
616
+ },
617
+ {
618
+ "content": "Team problem-solving dynamics: Establish psychological safety for open communication, define roles and responsibilities clearly, use structured problem-solving processes, encourage diverse perspectives, facilitate effective meetings, manage conflicts constructively, ensure equal participation, document decisions and action items, follow up on commitments, celebrate successes and learn from failures.",
619
+ "metadata": {
620
+ "source": "core_knowledge",
621
+ "type": "team_dynamics",
622
+ "topic": "team_problem_solving",
623
+ "chapter": "Team Collaboration for Problem Solving"
624
+ }
625
+ }
626
+ ]
627
+
628
+ # Create documents
629
+ documents = []
630
+ for item in default_knowledge:
631
+ doc = Document(
632
+ page_content=item["content"],
633
+ metadata=item["metadata"]
634
+ )
635
+ documents.append(doc)
636
+
637
+ # Create vectorstore
638
+ if documents and self.embeddings:
639
+ self.vectorstore = FAISS.from_documents(documents, self.embeddings)
640
+ self.stats['book_chunks'] = len(documents)
641
+ self.metadata['total_documents'] = len(documents)
642
+ self.metadata['book_processed'] = True
643
+ self.metadata['book_info'] = {
644
+ 'title': 'Core Problem-Solving Knowledge',
645
+ 'type': 'built_in',
646
+ 'chunks': len(documents)
647
+ }
648
+ return True
649
+ return False
650
+
651
+ def load_existing_knowledge(self):
652
+ """Load existing knowledge base from persistent storage"""
653
+ try:
654
+ # Load metadata
655
+ if self.metadata_path.exists():
656
+ with open(self.metadata_path, 'r') as f:
657
+ self.metadata = json.load(f)
658
+
659
+ # Load stats
660
+ if self.stats_path.exists():
661
+ with open(self.stats_path, 'r') as f:
662
+ self.stats = json.load(f)
663
+
664
+ # Load conversations
665
+ if self.conversations_path.exists():
666
+ with open(self.conversations_path, 'r') as f:
667
+ self.conversations = json.load(f)
668
+
669
+ # Load vectorstore
670
+ if self.vectorstore_path.exists() and self.embeddings:
671
+ self.vectorstore = FAISS.load_local(
672
+ str(self.vectorstore_path),
673
+ self.embeddings,
674
+ allow_dangerous_deserialization=True
675
+ )
676
+ return True
677
+ except Exception as e:
678
+ return False
679
+ return False
680
+
681
+ def save_all_data(self):
682
+ """Save all knowledge base data to persistent storage"""
683
+ try:
684
+ # Save metadata
685
+ self.metadata['last_updated'] = datetime.now().isoformat()
686
+ with open(self.metadata_path, 'w') as f:
687
+ json.dump(self.metadata, f, indent=2)
688
+
689
+ # Save stats
690
+ with open(self.stats_path, 'w') as f:
691
+ json.dump(self.stats, f, indent=2)
692
+
693
+ # Save conversations
694
+ with open(self.conversations_path, 'w') as f:
695
+ json.dump(self.conversations, f, indent=2)
696
+
697
+ # Save vectorstore
698
+ if self.vectorstore:
699
+ self.vectorstore.save_local(str(self.vectorstore_path))
700
+
701
+ return True
702
+ except Exception as e:
703
+ return False
704
+
705
+ def fallback_initialization(self):
706
+ """Fallback initialization if main process fails"""
707
+ self.create_default_knowledge()
708
+ self.metadata = {'fallback': True, 'created_at': datetime.now().isoformat()}
709
+ self.stats = {'total_queries': 0, 'learning_sessions': 0, 'book_chunks': 0, 'conversation_chunks': 0, 'silly_questions_blocked': 0}
710
+ self.conversations = []
711
+
712
+ def process_book_content(self, text, page_texts, book_info):
713
+ """Process book content and add to knowledge base"""
714
+ try:
715
+ # Text splitter
716
+ text_splitter = RecursiveCharacterTextSplitter(
717
+ chunk_size=800,
718
+ chunk_overlap=150,
719
+ length_function=len,
720
+ separators=["\n\n", "\n", ".", "!", "?", ",", " ", ""]
721
+ )
722
+
723
+ # Create documents
724
+ documents = []
725
+ for page_info in page_texts:
726
+ page_text = page_info['text']
727
+ chapter_title = self._extract_chapter_title(page_text.split('\n'))
728
+
729
+ doc = Document(
730
+ page_content=page_text,
731
+ metadata={
732
+ "source": "book",
733
+ "type": "book_content",
734
+ "page": page_info['page'],
735
+ "chapter": chapter_title,
736
+ "word_count": page_info['word_count'],
737
+ "book_title": book_info.get('title', 'Problem Solving Book'),
738
+ "processed_at": datetime.now().isoformat()
739
+ }
740
+ )
741
+ documents.append(doc)
742
+
743
+ # Split into chunks
744
+ chunks = text_splitter.split_documents(documents)
745
+
746
+ # Add to vectorstore
747
+ if self.vectorstore is None:
748
+ self.vectorstore = FAISS.from_documents(chunks, self.embeddings)
749
+ else:
750
+ new_vectorstore = FAISS.from_documents(chunks, self.embeddings)
751
+ self.vectorstore.merge_from(new_vectorstore)
752
+
753
+ # Update metadata
754
+ self.metadata['book_processed'] = True
755
+ self.metadata['book_info'] = book_info
756
+ self.metadata['total_documents'] += len(chunks)
757
+ self.stats['book_chunks'] += len(chunks)
758
+
759
+ return True, f"Successfully processed {len(chunks)} chunks from {len(page_texts)} pages!"
760
+
761
+ except Exception as e:
762
+ return False, f"Error processing book: {str(e)}"
763
+
764
+ def add_conversation_to_knowledge(self, question, answer):
765
+ """Add conversation to persistent knowledge base (auto-save)"""
766
+ if len(question.strip()) < 10 or len(answer.strip()) < 20:
767
+ return False
768
+
769
+ try:
770
+ conversation_text = f"Question: {question}\n\nAnswer: {answer}"
771
+
772
+ doc = Document(
773
+ page_content=conversation_text,
774
+ metadata={
775
+ "source": "learned_conversation",
776
+ "type": "qa_pair",
777
+ "question": question,
778
+ "answer_preview": answer[:200] + "..." if len(answer) > 200 else answer,
779
+ "conversation_id": hashlib.md5(conversation_text.encode()).hexdigest()[:8],
780
+ "added_at": datetime.now().isoformat(),
781
+ "quality_score": self._calculate_quality_score(question, answer)
782
+ }
783
+ )
784
+
785
+ # Add to vectorstore
786
+ if self.vectorstore is None:
787
+ self.vectorstore = FAISS.from_documents([doc], self.embeddings)
788
+ else:
789
+ new_vectorstore = FAISS.from_documents([doc], self.embeddings)
790
+ self.vectorstore.merge_from(new_vectorstore)
791
+
792
+ # Store conversation
793
+ self.conversations.append({
794
+ 'question': question,
795
+ 'answer': answer,
796
+ 'timestamp': datetime.now().isoformat(),
797
+ 'learned': True
798
+ })
799
+
800
+ # Update stats
801
+ self.stats['conversation_chunks'] += 1
802
+ self.stats['learning_sessions'] += 1
803
+ self.metadata['total_documents'] += 1
804
+
805
+ # Auto-save to persistent storage
806
+ self.save_all_data()
807
+
808
+ return True
809
+
810
+ except Exception as e:
811
+ return False
812
+
813
+ def search_knowledge_base(self, query, k=5):
814
+ """Search the persistent knowledge base"""
815
+ if self.vectorstore is None:
816
+ return []
817
+
818
+ try:
819
+ self.stats['total_queries'] += 1
820
+ docs = self.vectorstore.similarity_search_with_score(query, k=k)
821
+
822
+ results = []
823
+ for doc, score in docs:
824
+ result = {
825
+ 'content': doc.page_content,
826
+ 'source': doc.metadata.get('source', 'unknown'),
827
+ 'type': doc.metadata.get('type', 'unknown'),
828
+ 'page': doc.metadata.get('page', 'N/A'),
829
+ 'chapter': doc.metadata.get('chapter', 'Unknown Section'),
830
+ 'similarity_score': float(score),
831
+ 'metadata': doc.metadata
832
+ }
833
+ results.append(result)
834
+
835
+ return results
836
+
837
+ except Exception as e:
838
+ return []
839
+
840
+ def _extract_chapter_title(self, lines):
841
+ """Extract chapter title from text lines"""
842
+ for line in lines[:10]:
843
+ line = line.strip()
844
+ if line and len(line) < 100:
845
+ if re.match(r'(chapter|section|part|unit)\s+\d+', line.lower()):
846
+ return line
847
+ if line.isupper() or line.istitle():
848
+ return line
849
+ return "General Content"
850
+
851
+ def _calculate_quality_score(self, question, answer):
852
+ """Calculate conversation quality score"""
853
+ score = 0
854
+
855
+ # Question quality
856
+ if len(question.split()) >= 5: score += 1
857
+ if any(word in question.lower() for word in ['how', 'what', 'why', 'strategy', 'problem']): score += 1
858
+ if '?' in question: score += 1
859
+
860
+ # Answer quality
861
+ if len(answer.split()) >= 20: score += 1
862
+ if any(word in answer.lower() for word in ['approach', 'solution', 'method', 'step']): score += 1
863
+
864
+ return min(score, 5)
865
+
866
+ class AshokMinimalChatbot:
867
+ def __init__(self):
868
+ self.knowledge_base = PersistentHFKnowledgeBase()
869
+
870
+ def is_silly_question(self, question):
871
+ """Detect silly or irrelevant questions"""
872
+ question_lower = question.lower().strip()
873
+
874
+ if len(question_lower) < 3:
875
+ return True
876
+
877
+ # Greeting patterns
878
+ greeting_patterns = [
879
+ r'\b(hello|hi|hey|salam|namaste|adab)\b',
880
+ r'\b(good morning|evening|afternoon)\b',
881
+ r'\b(how are you|kaise ho|kya hal)\b',
882
+ r'\b(what.*your name|who are you)\b'
883
+ ]
884
+
885
+ # Silly keywords
886
+ silly_keywords = [
887
+ 'stupid', 'dumb', 'joke', 'funny', 'lol', 'weather', 'movie',
888
+ 'song', 'game', 'gossip', 'love', 'dating', 'facebook',
889
+ 'instagram', 'politics', 'religion', 'age', 'appearance'
890
+ ]
891
+
892
+ # Problem-solving keywords
893
+ good_keywords = [
894
+ 'problem', 'solve', 'solution', 'strategy', 'approach',
895
+ 'method', 'challenge', 'decision', 'plan', 'analyze',
896
+ 'conflict', 'team', 'work', 'project', 'manage'
897
+ ]
898
+
899
+ # Check patterns
900
+ for pattern in greeting_patterns:
901
+ if re.search(pattern, question_lower):
902
+ return True
903
+
904
+ # Score keywords
905
+ good_score = sum(1 for keyword in good_keywords if keyword in question_lower)
906
+ if good_score >= 2:
907
+ return False
908
+
909
+ silly_score = sum(1 for keyword in silly_keywords if keyword in question_lower)
910
+ if silly_score >= 1:
911
+ return True
912
+
913
+ # Check structure
914
+ question_words = ['what', 'how', 'why', 'when', 'where', 'can', 'should']
915
+ has_question_word = any(word in question_lower.split() for word in question_words)
916
+ word_count = len(question_lower.split())
917
+
918
+ if word_count < 4 and not has_question_word:
919
+ return True
920
+
921
+ return False
922
+
923
+ def generate_response(self, question, api_key):
924
+ """Generate response using Gemini"""
925
+ try:
926
+ # Check for silly questions
927
+ if self.is_silly_question(question):
928
+ self.knowledge_base.stats['silly_questions_blocked'] += 1
929
+
930
+ silly_responses = [
931
+ "Abay Sallay! Don't waste my time with such bakwas. Ask me something related to problem solving yaar!",
932
+ "Abay Sallay! Ye kya timepass hai? I'm here to help with problem solving, not for chit-chat. Be serious!",
933
+ "Abay Sallay! Focus on real problems that need solving, samjha? Ask about strategies and approaches!",
934
+ "Abay Sallay! This is a problem-solving platform. Ask me about challenges, decisions, ya conflict resolution!",
935
+ "Abay Sallay! Tumhara dimagh kahan hai? Ask meaningful questions about problem-solving techniques, yaar!"
936
+ ]
937
+
938
+ import random
939
+ return random.choice(silly_responses), False
940
+
941
+ # Configure Gemini
942
+ genai.configure(api_key=api_key)
943
+ model = genai.GenerativeModel('gemini-2.0-flash')
944
+
945
+ # Search knowledge base
946
+ relevant_results = self.knowledge_base.search_knowledge_base(question, k=5)
947
+
948
+ # Build context
949
+ context = ""
950
+ references = []
951
+
952
+ if relevant_results:
953
+ context = "=== RELEVANT KNOWLEDGE ===\n\n"
954
+ for i, result in enumerate(relevant_results, 1):
955
+ source_type = result['type']
956
+ if source_type == 'book_content':
957
+ context += f"**Reference {i}** (Chapter: {result['chapter']}, Page: {result['page']}):\n"
958
+ elif source_type == 'qa_pair':
959
+ context += f"**Learning {i}** (From past conversations):\n"
960
+ else:
961
+ context += f"**Framework {i}** (Core knowledge):\n"
962
+
963
+ context += f"{result['content']}\n\n"
964
+ references.append(result)
965
+
966
+ # Create prompt
967
+ prompt = f"""
968
+ You are Ashok, a problem-solving expert with Pakistani/Indian conversational style.
969
+
970
+ Your characteristics:
971
+ 1. Mix English with Urdu naturally: "yaar", "acha", "bilkul", "samjha", "dekho"
972
+ 2. Enthusiastic responses: "Excellent question yaar!" or "Bahut acha sawal!"
973
+ 3. Reference knowledge sources when available
974
+ 4. Provide practical, actionable advice
975
+ 5. Encouraging and professional tone
976
+
977
+ {context}
978
+
979
+ User Question: {question}
980
+
981
+ Provide a comprehensive, practical response using your characteristic style.
982
+ Reference the knowledge sources when relevant and give actionable steps.
983
+ """
984
+
985
+ response = model.generate_content(prompt)
986
+ final_response = response.text
987
+
988
+ # Add clean references section
989
+ if references:
990
+ final_response += "\n\n**Knowledge Sources:**\n"
991
+ for ref in references:
992
+ if ref['type'] == 'book_content':
993
+ final_response += f"• Book: {ref['chapter']} (Page {ref['page']})\n"
994
+ elif ref['type'] == 'qa_pair':
995
+ final_response += f"• Previous Learning\n"
996
+ else:
997
+ final_response += f"• Core Framework: {ref['metadata'].get('topic', 'Problem-solving')}\n"
998
+
999
+ return final_response, True
1000
+
1001
+ except Exception as e:
1002
+ return f"Sorry yaar, I encountered an error: {str(e)}. Please check your API key and try again!", False
1003
+
1004
+ def show_typing_indicator():
1005
+ """Show typing indicator"""
1006
+ st.markdown("""
1007
+ <div class="typing-indicator">
1008
+ <span>Ashok is thinking</span>
1009
+ <div class="typing-dots">
1010
+ <div class="typing-dot"></div>
1011
+ <div class="typing-dot"></div>
1012
+ <div class="typing-dot"></div>
1013
+ </div>
1014
+ </div>
1015
+ """, unsafe_allow_html=True)
1016
+
1017
+ def show_api_setup():
1018
+ """Show API key setup interface"""
1019
+ st.markdown("""
1020
+ <div class="api-setup-container">
1021
+ <div class="api-setup-title">Welcome to Ashok 2.0</div>
1022
+ <div class="api-setup-subtitle">To get started, please enter your Gemini API key</div>
1023
+ </div>
1024
+ """, unsafe_allow_html=True)
1025
+
1026
+ # API key input
1027
+ api_key = st.text_input(
1028
+ "Gemini API Key",
1029
+ type="password",
1030
+ placeholder="Enter your API key here...",
1031
+ label_visibility="collapsed"
1032
+ )
1033
+
1034
+ # API guide
1035
+ st.markdown("""
1036
+ <div class="api-guide">
1037
+ <div class="api-guide-title">How to get your free API key:</div>
1038
+ <div class="api-guide-text">1. Visit <a href="https://makersuite.google.com/app/apikey" target="_blank" class="api-link">Google AI Studio</a></div>
1039
+ <div class="api-guide-text">2. Sign in with your Google account</div>
1040
+ <div class="api-guide-text">3. Click "Create API Key"</div>
1041
+ <div class="api-guide-text">4. Copy and paste the key above</div>
1042
+ </div>
1043
+ """, unsafe_allow_html=True)
1044
+
1045
+ return api_key
1046
+
1047
+ def show_quick_actions():
1048
+ """Show enhanced quick action buttons"""
1049
+ st.markdown("""
1050
+ <div class="quick-actions-container">
1051
+ <div class="quick-actions-title">Quick Start Questions</div>
1052
+ <div class="quick-action-grid">
1053
+ """, unsafe_allow_html=True)
1054
+
1055
+ # Quick action data
1056
+ actions = [
1057
+ {
1058
+ "icon": "🎯",
1059
+ "title": "Task Prioritization",
1060
+ "desc": "Learn how to prioritize when everything seems urgent",
1061
+ "question": "How do I prioritize tasks when everything seems urgent and important?"
1062
+ },
1063
+ {
1064
+ "icon": "🤝",
1065
+ "title": "Conflict Resolution",
1066
+ "desc": "Effective strategies for resolving team conflicts",
1067
+ "question": "What's the best approach to resolve conflicts in my team?"
1068
+ },
1069
+ {
1070
+ "icon": "⚡",
1071
+ "title": "Decision Making",
1072
+ "desc": "Improve your decision-making process",
1073
+ "question": "How can I improve my decision-making process for complex problems?"
1074
+ },
1075
+ {
1076
+ "icon": "🎪",
1077
+ "title": "Difficult People",
1078
+ "desc": "Handle challenging stakeholders professionally",
1079
+ "question": "How do I deal with difficult stakeholders effectively?"
1080
+ },
1081
+ {
1082
+ "icon": "🔄",
1083
+ "title": "Change Management",
1084
+ "desc": "Navigate organizational changes smoothly",
1085
+ "question": "How can I help my team adapt to organizational changes?"
1086
+ },
1087
+ {
1088
+ "icon": "💡",
1089
+ "title": "Creative Solutions",
1090
+ "desc": "Generate innovative solutions to problems",
1091
+ "question": "What techniques can I use to think more creatively about problems?"
1092
+ }
1093
+ ]
1094
+
1095
+ # Create columns for actions
1096
+ cols = st.columns(2)
1097
+ for i, action in enumerate(actions):
1098
+ with cols[i % 2]:
1099
+ if st.button(
1100
+ f"{action['icon']} {action['title']}",
1101
+ key=f"action_{i}",
1102
+ help=action['desc'],
1103
+ use_container_width=True
1104
+ ):
1105
+ st.session_state.auto_question = action['question']
1106
+ st.rerun()
1107
+
1108
+ st.markdown("</div></div>", unsafe_allow_html=True)
1109
+
1110
+ def test_api_key(api_key):
1111
+ """Test if API key is valid"""
1112
+ try:
1113
+ genai.configure(api_key=api_key)
1114
+ model = genai.GenerativeModel('gemini-2.0-flash')
1115
+ test_response = model.generate_content("Hello")
1116
+ return True
1117
+ except Exception as e:
1118
+ return False
1119
+
1120
+ # Global instance for the app
1121
+ @st.cache_resource
1122
+ def get_chatbot():
1123
+ """Get cached chatbot instance"""
1124
+ return AshokMinimalChatbot()
1125
+
1126
+ def main():
1127
+ # Get cached chatbot instance
1128
+ chatbot = get_chatbot()
1129
+
1130
+ # Initialize session state
1131
+ if 'messages' not in st.session_state:
1132
+ st.session_state.messages = []
1133
+
1134
+ if 'auto_question' not in st.session_state:
1135
+ st.session_state.auto_question = None
1136
+
1137
+ if 'api_key_valid' not in st.session_state:
1138
+ st.session_state.api_key_valid = False
1139
+
1140
+ # Main title
1141
+ st.markdown('<h1 class="main-title">ASHOK 2.0</h1>', unsafe_allow_html=True)
1142
+
1143
+ # API Key Setup Phase
1144
+ if not st.session_state.api_key_valid:
1145
+ api_key = show_api_setup()
1146
+
1147
+ if api_key:
1148
+ if test_api_key(api_key):
1149
+ st.session_state.api_key = api_key
1150
+ st.session_state.api_key_valid = True
1151
+ st.markdown("""
1152
+ <div class="status-success">
1153
+ API key configured successfully! Ready to chat.
1154
+ </div>
1155
+ """, unsafe_allow_html=True)
1156
+ time.sleep(1)
1157
+ st.rerun()
1158
+ else:
1159
+ st.markdown("""
1160
+ <div class="status-warning">
1161
+ Invalid API key. Please check and try again.
1162
+ </div>
1163
+ """, unsafe_allow_html=True)
1164
+
1165
+ # Main Chat Interface Phase
1166
+ else:
1167
+ api_key = st.session_state.api_key
1168
+
1169
+ # Handle auto questions from quick actions
1170
+ if st.session_state.auto_question:
1171
+ # Add user message
1172
+ st.session_state.messages.append({"role": "user", "content": st.session_state.auto_question})
1173
+
1174
+ # Generate response
1175
+ with st.spinner("Processing your question..."):
1176
+ response, is_helpful = chatbot.generate_response(st.session_state.auto_question, api_key)
1177
+ st.session_state.messages.append({"role": "assistant", "content": response})
1178
+
1179
+ # Auto-learn from helpful conversations
1180
+ if is_helpful and not chatbot.is_silly_question(st.session_state.auto_question):
1181
+ chatbot.knowledge_base.add_conversation_to_knowledge(st.session_state.auto_question, response)
1182
+
1183
+ # Clear auto question
1184
+ st.session_state.auto_question = None
1185
+ st.rerun()
1186
+
1187
+ # Display chat history
1188
+ if st.session_state.messages:
1189
+ st.markdown('<div class="chat-container">', unsafe_allow_html=True)
1190
+ for message in st.session_state.messages:
1191
+ with st.chat_message(message["role"]):
1192
+ st.markdown(message["content"])
1193
+ st.markdown('</div>', unsafe_allow_html=True)
1194
+
1195
+ # Chat input
1196
+ if prompt := st.chat_input("Ask about problem-solving strategies...", key="main_chat"):
1197
+ # Add user message
1198
+ st.session_state.messages.append({"role": "user", "content": prompt})
1199
+ with st.chat_message("user"):
1200
+ st.markdown(prompt)
1201
+
1202
+ # Generate response with typing indicator
1203
+ with st.chat_message("assistant"):
1204
+ # Show typing indicator
1205
+ typing_placeholder = st.empty()
1206
+ with typing_placeholder:
1207
+ show_typing_indicator()
1208
+
1209
+ # Simulate typing delay
1210
+ time.sleep(1)
1211
+
1212
+ # Clear typing indicator and show response
1213
+ typing_placeholder.empty()
1214
+
1215
+ with st.spinner("Processing..."):
1216
+ response, is_helpful = chatbot.generate_response(prompt, api_key)
1217
+ st.markdown(response)
1218
+
1219
+ # Add to chat history
1220
+ st.session_state.messages.append({"role": "assistant", "content": response})
1221
+
1222
+ # Auto-learn from helpful conversations
1223
+ if is_helpful and not chatbot.is_silly_question(prompt):
1224
+ learned = chatbot.knowledge_base.add_conversation_to_knowledge(prompt, response)
1225
+ if learned:
1226
+ st.markdown(
1227
+ '<div class="learning-indicator">Knowledge updated - This conversation has been learned!</div>',
1228
+ unsafe_allow_html=True
1229
+ )
1230
+
1231
+ # Show quick actions if no messages yet
1232
+ if not st.session_state.messages:
1233
+ show_quick_actions()
1234
+
1235
+ # Control panel
1236
+ if st.session_state.messages:
1237
+ st.markdown("""
1238
+ <div class="control-panel">
1239
+ """, unsafe_allow_html=True)
1240
+
1241
+ if st.button("Clear Chat", key="clear_chat"):
1242
+ st.session_state.messages = []
1243
+ st.rerun()
1244
+
1245
+ st.markdown("</div>", unsafe_allow_html=True)
1246
+
1247
+ if __name__ == "__main__":
1248
+ main()