Sazid2 commited on
Commit
0bff093
·
verified ·
1 Parent(s): 56f5513

Create aap.pyyy

Browse files
Files changed (1) hide show
  1. aap.pyyy +1230 -0
aap.pyyy ADDED
@@ -0,0 +1,1230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import os
4
+ from datetime import datetime, timedelta
5
+ import re
6
+ import hashlib
7
+ import json
8
+
9
+ # ===============================
10
+ # HUGGING FACE API KEY HANDLING
11
+ # ===============================
12
+ # Try to get API key from Hugging Face secrets first
13
+ try:
14
+ # For Hugging Face Spaces
15
+ from huggingface_hub import HfFolder
16
+ api_key = HfFolder.get_token()
17
+ if not api_key:
18
+ # Try environment variable
19
+ api_key = os.environ.get("DEEPSEEK_API_KEY", "")
20
+ except ImportError:
21
+ # huggingface_hub not installed
22
+ api_key = os.environ.get("DEEPSEEK_API_KEY", "")
23
+ except:
24
+ # Any other error
25
+ api_key = os.environ.get("DEEPSEEK_API_KEY", "")
26
+
27
+ # ===============================
28
+ # HUGGING FACE COMPATIBLE CACHE CLASS
29
+ # ===============================
30
+ class HybridCache:
31
+ def __init__(self, ttl_hours=24, max_entries=100):
32
+ """
33
+ In-memory only cache for Hugging Face Spaces
34
+ - No file system access needed
35
+ - Works within session only
36
+ """
37
+ self.ttl_hours = ttl_hours
38
+ self.max_entries = max_entries
39
+ self.cache = {} # Simple dictionary for in-memory storage
40
+
41
+ def get(self, cache_key):
42
+ """Get cached answer if not expired"""
43
+ if cache_key in self.cache:
44
+ entry = self.cache[cache_key]
45
+ created_at = entry.get('created_at')
46
+
47
+ if isinstance(created_at, str):
48
+ created_at = datetime.fromisoformat(created_at)
49
+
50
+ # Check TTL (in hours for Hugging Face)
51
+ if created_at and (datetime.now() - created_at).seconds < (self.ttl_hours * 3600):
52
+ # Update access time
53
+ entry['last_accessed'] = datetime.now().isoformat()
54
+ entry['access_count'] = entry.get('access_count', 0) + 1
55
+ return entry
56
+ else:
57
+ # Remove expired entry
58
+ del self.cache[cache_key]
59
+
60
+ return None
61
+
62
+ def set(self, cache_key, data):
63
+ """Add or update cache entry"""
64
+ data['created_at'] = datetime.now().isoformat()
65
+ data['last_accessed'] = data['created_at']
66
+ data['access_count'] = 1
67
+
68
+ self.cache[cache_key] = data
69
+
70
+ # Enforce max entries limit
71
+ if len(self.cache) > self.max_entries:
72
+ # Remove oldest entries (by access time)
73
+ sorted_keys = sorted(
74
+ self.cache.keys(),
75
+ key=lambda k: self.cache[k].get('last_accessed', ''),
76
+ reverse=True
77
+ )
78
+ for key in sorted_keys[self.max_entries:]:
79
+ del self.cache[key]
80
+
81
+ def clear_expired(self):
82
+ """Manually clear expired entries"""
83
+ current_time = datetime.now()
84
+ expired_keys = []
85
+
86
+ for key, entry in self.cache.items():
87
+ created_at = entry.get('created_at')
88
+ if isinstance(created_at, str):
89
+ created_at = datetime.fromisoformat(created_at)
90
+
91
+ if created_at and (current_time - created_at).seconds >= (self.ttl_hours * 3600):
92
+ expired_keys.append(key)
93
+
94
+ for key in expired_keys:
95
+ del self.cache[key]
96
+
97
+ return len(expired_keys)
98
+
99
+ def clear_all(self):
100
+ """Clear all cache entries"""
101
+ self.cache = {}
102
+
103
+ def get_stats(self):
104
+ """Get cache statistics"""
105
+ total_saved_tokens = sum(entry.get('tokens', 0) for entry in self.cache.values())
106
+ total_access_count = sum(entry.get('access_count', 0) for entry in self.cache.values())
107
+ avg_age_hours = 0
108
+
109
+ if self.cache:
110
+ current_time = datetime.now()
111
+ ages = []
112
+ for entry in self.cache.values():
113
+ created_at = entry.get('created_at')
114
+ if isinstance(created_at, str):
115
+ created_at = datetime.fromisoformat(created_at)
116
+ if created_at:
117
+ ages.append((current_time - created_at).seconds / 3600) # in hours
118
+ avg_age_hours = sum(ages) / len(ages) if ages else 0
119
+
120
+ return {
121
+ 'total_entries': len(self.cache),
122
+ 'total_saved_tokens': total_saved_tokens,
123
+ 'total_access_count': total_access_count,
124
+ 'average_age_hours': round(avg_age_hours, 1),
125
+ 'ttl_hours': self.ttl_hours,
126
+ 'max_entries': self.max_entries,
127
+ 'storage_mode': 'In-Memory (Hugging Face)'
128
+ }
129
+
130
+ # Page config - must be first Streamlit command
131
+ st.set_page_config(
132
+ page_title="SEBA দশম শ্ৰেণীৰ AI টিউটাৰ",
133
+ page_icon="🎓",
134
+ layout="wide",
135
+ initial_sidebar_state="collapsed"
136
+ )
137
+
138
+ # Simplified CSS with reduced spacing (50% of original)
139
+ st.markdown("""
140
+ <style>
141
+ /* Assamese-friendly fonts */
142
+ @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Bengali:wght@400;500;600;700;800&family=Hind+Siliguri:wght@300;400;500;600;700&display=swap');
143
+
144
+ * {
145
+ font-family: 'Noto Sans Bengali', 'Hind Siliguri', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
146
+ }
147
+
148
+ /* Reduced spacing header */
149
+ .header-container {
150
+ background: linear-gradient(135deg, #0d47a1 0%, #1565c0 50%, #1976d2 100%);
151
+ padding: 1.25rem;
152
+ border-radius: 15px;
153
+ margin-bottom: 1rem;
154
+ color: white;
155
+ position: relative;
156
+ overflow: hidden;
157
+ border: 1px solid rgba(255,255,255,0.1);
158
+ box-shadow: 0 5px 15px rgba(13,71,161,0.2);
159
+ }
160
+
161
+ /* The top rainbow line */
162
+ .header-container::before {
163
+ content: '';
164
+ position: absolute;
165
+ top: 0;
166
+ left: 0;
167
+ right: 0;
168
+ height: 3px;
169
+ background: linear-gradient(90deg, #FF5722, #FF9800, #4CAF50);
170
+ }
171
+
172
+ /* Header text */
173
+ .header-container h1 {
174
+ color: #ffffff;
175
+ font-size: 1.5rem;
176
+ font-weight: 800;
177
+ text-shadow: 0px 1px 3px rgba(0,0,0,0.5);
178
+ margin: 0;
179
+ line-height: 1.2;
180
+ }
181
+
182
+ .header-container p {
183
+ color: #f6f9ff;
184
+ font-weight: 600;
185
+ font-size: 0.95rem;
186
+ opacity: 1 !important;
187
+ text-shadow: 0px 1px 2px rgba(0,0,0,0.4);
188
+ margin-top: .2rem;
189
+ }
190
+
191
+ .subject-card {
192
+ background: linear-gradient(145deg, #ffffff 0%, #f0f7ff 100%);
193
+ padding: 0.75rem;
194
+ border-radius: 10px;
195
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
196
+ border-left: 4px solid #2196F3;
197
+ margin: 0.5rem 0;
198
+ transition: all 0.3s ease;
199
+ border: 1px solid #e3f2fd;
200
+ }
201
+
202
+ .subject-card:hover {
203
+ transform: translateY(-3px);
204
+ box-shadow: 0 5px 12px rgba(33, 150, 243, 0.15);
205
+ }
206
+
207
+ .answer-box {
208
+ background: linear-gradient(145deg, #f8fdff 0%, #ffffff 100%);
209
+ padding: 1rem;
210
+ border-radius: 10px;
211
+ border: 1px solid #e1f5fe;
212
+ margin: 0.75rem 0;
213
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
214
+ position: relative;
215
+ }
216
+
217
+ .stButton > button {
218
+ background: linear-gradient(135deg, #FF5722 0%, #FF9800 100%);
219
+ color: white;
220
+ border: none;
221
+ padding: 0.4rem 1rem;
222
+ border-radius: 8px;
223
+ font-weight: 600;
224
+ font-size: 0.9rem;
225
+ transition: all 0.3s;
226
+ box-shadow: 0 2px 6px rgba(255, 87, 34, 0.3);
227
+ }
228
+
229
+ .stButton > button:hover {
230
+ transform: translateY(-1px);
231
+ box-shadow: 0 3px 9px rgba(255, 87, 34, 0.4);
232
+ }
233
+
234
+ .sidebar-section {
235
+ background: linear-gradient(145deg, #f8f9fa 0%, #e3f2fd 100%);
236
+ padding: 0.75rem;
237
+ border-radius: 10px;
238
+ margin-bottom: 0.75rem;
239
+ border: 1px solid #bbdefb;
240
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
241
+ }
242
+
243
+ .assamese-highlight {
244
+ background: linear-gradient(120deg, #FFF176 0%, #FFEB3B 100%);
245
+ background-repeat: no-repeat;
246
+ background-size: 100% 0.3em;
247
+ background-position: 0 90%;
248
+ padding: 0.1rem 0.2rem;
249
+ font-weight: 700;
250
+ color: #FF6F00;
251
+ }
252
+
253
+ .assamese-text {
254
+ font-family: 'Noto Sans Bengali', sans-serif;
255
+ font-weight: 500;
256
+ color: #0d47a1;
257
+ line-height: 1.4;
258
+ }
259
+
260
+ .assamese-title {
261
+ font-family: 'Noto Sans Bengali', sans-serif;
262
+ font-weight: 700;
263
+ color: #1565c0;
264
+ }
265
+
266
+ /* Chat bubble styling */
267
+ .user-bubble {
268
+ background: linear-gradient(135deg, #2196F3 0%, #0d47a1 100%) !important;
269
+ color: white;
270
+ padding: 0.5rem 0.75rem;
271
+ border-radius: 12px 12px 0 12px;
272
+ max-width: 80%;
273
+ box-shadow: 0 2px 6px rgba(33, 150, 243, 0.2);
274
+ margin-left: auto;
275
+ }
276
+
277
+ .ai-bubble {
278
+ background: linear-gradient(135deg, #f5f5f5 0%, #ffffff 100%) !important;
279
+ padding: 0.75rem;
280
+ border-radius: 12px 12px 12px 0;
281
+ border: 1px solid #e0e0e0;
282
+ box-shadow: 0 2px 6px rgba(0,0,0,0.05);
283
+ }
284
+
285
+ /* Chat container */
286
+ .chat-container {
287
+ margin-bottom: 1rem;
288
+ }
289
+
290
+ .chat-message {
291
+ margin-bottom: 0.75rem;
292
+ animation: fadeIn 0.3s ease-in;
293
+ }
294
+
295
+ @keyframes fadeIn {
296
+ from { opacity: 0; transform: translateY(5px); }
297
+ to { opacity: 1; transform: translateY(0); }
298
+ }
299
+
300
+ /* LaTeX equation styling */
301
+ .katex {
302
+ font-size: 1em !important;
303
+ padding: 0.1rem 0.25rem;
304
+ background: rgba(33, 150, 243, 0.1);
305
+ border-radius: 3px;
306
+ margin: 0.1rem 0;
307
+ }
308
+
309
+ /* Control panel styling */
310
+ .control-panel {
311
+ background: linear-gradient(145deg, #f8f9fa 0%, #e3f2fd 100%);
312
+ padding: 1rem;
313
+ border-radius: 15px;
314
+ margin: 1rem 0;
315
+ border: 1px solid #bbdefb;
316
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
317
+ }
318
+
319
+ /* Streaming text animation */
320
+ .streaming-text {
321
+ display: inline-block;
322
+ }
323
+
324
+ .streaming-text::after {
325
+ content: '▋';
326
+ animation: cursor-blink 1s infinite;
327
+ font-weight: bold;
328
+ color: #2196F3;
329
+ }
330
+
331
+ @keyframes cursor-blink {
332
+ 0%, 100% { opacity: 1; }
333
+ 50% { opacity: 0; }
334
+ }
335
+
336
+ /* Progress indicator */
337
+ .progress-indicator {
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+ gap: 0.5rem;
342
+ color: #0d47a1;
343
+ font-weight: 600;
344
+ padding: 0.5rem;
345
+ }
346
+
347
+ .thinking-dots {
348
+ display: flex;
349
+ gap: 0.2rem;
350
+ }
351
+
352
+ .thinking-dots span {
353
+ width: 0.4rem;
354
+ height: 0.4rem;
355
+ border-radius: 50%;
356
+ background: #2196F3;
357
+ animation: thinking 1.4s infinite ease-in-out;
358
+ }
359
+
360
+ .thinking-dots span:nth-child(1) { animation-delay: -0.32s; }
361
+ .thinking-dots span:nth-child(2) { animation-delay: -0.16s; }
362
+ .thinking-dots span:nth-child(3) { animation-delay: 0s; }
363
+
364
+ @keyframes thinking {
365
+ 0%, 80%, 100% { transform: scale(0); }
366
+ 40% { transform: scale(1); }
367
+ }
368
+
369
+ /* Cache indicator styling */
370
+ .cache-badge {
371
+ background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
372
+ color: white;
373
+ padding: 0.2rem 0.5rem;
374
+ border-radius: 12px;
375
+ font-size: 0.75rem;
376
+ font-weight: 600;
377
+ display: inline-flex;
378
+ align-items: center;
379
+ gap: 0.2rem;
380
+ }
381
+
382
+ /* Responsive adjustments */
383
+ @media (max-width: 768px) {
384
+ .header-container {
385
+ padding: 0.75rem;
386
+ }
387
+ .subject-card {
388
+ padding: 0.5rem;
389
+ }
390
+ .user-bubble, .ai-bubble {
391
+ max-width: 90%;
392
+ }
393
+ .control-panel {
394
+ padding: 0.75rem;
395
+ }
396
+ }
397
+ </style>
398
+ """, unsafe_allow_html=True)
399
+
400
+ # ===============================
401
+ # SEBA CURRICULUM DATA
402
+ # ===============================
403
+ SEBA_CURRICULUM = {
404
+ "📐 গণিত (Mathematics)": {
405
+ "অধ্যায় ১": "বাস্তৱ সংখ্যা (Real Numbers)",
406
+ "অধ্যায় ২": "বহুপদ (Polynomials)",
407
+ "অধ্যায় ৩": "দ্বিঘাত সমীকৰণ (Quadratic Equations)",
408
+ "অধ্যায় ৪": "সামান্তৰিক শ্রেণী (Arithmetic Progressions)",
409
+ "অধ্যায় ৫": "ত্ৰিভুজ (Triangles)",
410
+ "অধ্যায় ৬": "ত্রিকোণমিতি (Trigonometry)",
411
+ "অধ্যায় ৭": "বৃত্ত (Circles)",
412
+ "অধ্যায় ৮": "স্থানাঙ্ক জ্যামিতি (Coordinate Geometry)",
413
+ "অধ্যায় ৯": "ক্ষেত্রফল আৰু আয়তন (Areas and Volumes)",
414
+ "অধ্যায় ১০": "পৰিসংখ্যা (Statistics)",
415
+ "অধ্যায় ১১": "সম্ভাৱিতা (Probability)"
416
+ },
417
+ "🔬 বিজ্ঞান (Science)": {
418
+ "অধ্যায় ১": "ৰাসায়নিক বিক্রিয়া আৰু সমীকৰণ",
419
+ "অধ্যায় ২": "এছিড, ক্ষাৰক আৰু লৱণ",
420
+ "অধ্যায় ৩": "ধাতু আৰু অধাতু",
421
+ "অধ্যায় ৪": "কার্বন আৰু তাৰ যৌগ",
422
+ "অধ্যায় ৫": "পৰ্যাবৃত্ত শ্রেণীবিভাজন",
423
+ "অধ্যায় ৬": "জীৱন প্ৰক্ৰিয়া",
424
+ "অধ্যায় ৭": "নিয়ন্ত্ৰণ আৰু সমন্বয়",
425
+ "অধ্যায় ৮": "জীৱই কেনেদৰে বংশবিস্তাৰ কৰে",
426
+ "অধ্যায় ৯": "আনুভূমিক আৰু ঊর্ধ্বমুখী বংশগতি",
427
+ "অধ্যায় ১০": "পোহৰ-প্ৰতিফলন আৰু প্ৰতিসৰণ",
428
+ "অধ্যায় ১১": "মানুহৰ চকু আৰু বৰ্ণিল পৃথিৱী",
429
+ "অধ্যায় ১২": "বিদ্যুৎ",
430
+ "অধ্যায় ১৩": "বিদ্যুৎ-চুম্বকীয় প্ৰভাৱ",
431
+ "অধ্যায় ১৪": "শক্তিৰ উৎসসমূহ",
432
+ "অধ্যায় ১৫": "আমাৰ পৰিৱেশ",
433
+ "অধ্যায় ১৬": "প্রাকৃতিক সম্পদৰ ব্যৱস্থাপনা"
434
+ },
435
+ "🌍 সমাজ বিজ্ঞান (Social Science)": {
436
+ "অধ্যায় ১": "ইউৰোপত ৰাষ্ট্ৰবাদৰ উত্থান",
437
+ "অধ্যায় ২": "ভাৰতীয় জাতীয়তাবাদৰ উত্থান",
438
+ "অধ্যায় ৩": "ভূগোল-প্রাকৃতিক আৰু মানৱ",
439
+ "অধ্যায় ৪": "অৰ্থনীতি-উন্নয়ন",
440
+ "অধ্যায় ৫": "লোকসাধাৰণৰ সংস্কৃতি আৰু জাতীয়তাবাদ",
441
+ "অধ্যায় ৬": "উদ্যোগ",
442
+ "অধ্যায় ৭": "অৰ্থনৈতিক অৱস্থা",
443
+ "অধ্যায় ৮": "ৰাজনৈতিক দল",
444
+ "অধ্যায় ৯": "ক্ষমতাৰ ভাগ-বতৰা",
445
+ "অধ্যায় ১০": "জনসম্পদ"
446
+ },
447
+ "📖 ইংৰাজী (English)": {
448
+ "পাঠ ১": "A Letter to God",
449
+ "পাঠ ২": "Nelson Mandela: Long Walk to Freedom",
450
+ "পাঠ ৩": "Two Stories about Flying",
451
+ "পাঠ ৪": "From the Diary of Anne Frank",
452
+ "পাঠ ৫": "The Hundred Dresses – I",
453
+ "পাঠ ৬": "The Hundred Dresses – II",
454
+ "পাঠ ৭": "Glimpses of India",
455
+ "পাঠ ৮": "Mijbil the Otter",
456
+ "পাঠ ৯": "Madam Rides the Bus",
457
+ "পাঠ ১০": "The Sermon at Benares",
458
+ "পাঠ ১১": "The Proposal"
459
+ },
460
+ "📜 অসমীয়া (Assamese)": {
461
+ "পাঠ ১": "বৰগীত",
462
+ "পাঠ ২": "জীৱন-সঙ্গীত",
463
+ "পাঠ ৩": "প্রশস্তি",
464
+ "পাঠ ৪": "মোৰ মৰমি জনমভূমি",
465
+ "পাঠ ৫": "অসমীয়া ভাষাৰ উন্নতি",
466
+ "পাঠ ৬": "অসমৰ লোক-সংস্কৃতি",
467
+ "পাঠ ৭": "আমাৰ ঋতু",
468
+ "পাঠ ৮": "বহাগ বিহু",
469
+ "পাঠ ৯": "মহাপুরুষীয়া ধৰ্ম",
470
+ "পাঠ ১০": "সাহিত্যৰ ৰূপ"
471
+ },
472
+ "📘 হিন্দী (Hindi)": {
473
+ "पाठ १": "साखी",
474
+ "पाठ २": "पद",
475
+ "पाठ ३": "दोहे",
476
+ "पाठ ४": "मनुष्यता",
477
+ "पाठ ५": "पर्वत प्रदेश में पावस",
478
+ "पाठ ६": "मधुर-मधुर मेरे दीपक जल",
479
+ "पाठ ७": "तोप",
480
+ "पाठ ८": "कर चले हम फ़िदा",
481
+ "पाठ ९": "आत्मत्राण",
482
+ "पाठ १०": "बड़े भाई साहब"
483
+ }
484
+ }
485
+
486
+ # Subject-wise prompt templates
487
+ SUBJECT_PROMPTS = {
488
+ "📐 গণিত (Mathematics)": {
489
+ "base_prompt": """তুমি এজন বিশেষজ্ঞ গণিত শিক্ষক। SEBA দশম শ্ৰেণীৰ গণিতৰ পাঠ্যপুথিৰ {chapter_name} অধ্যায়ত থকা সকলো ধাৰণা, সূত্ৰ, আৰু উদাহৰণ তুমি ভালকৈ জানা।
490
+
491
+ **গণিতৰ বিশেষ নিৰ্দেশনা:**
492
+ ১. **সকলো সূত্ৰ LaTeX ফৰ্মেটত দিবা**: $formula$ (দুয়োটা $ চিহ্নৰ মাজত)
493
+ ২. **ধাপে ধাপে সমাধান দেখুৱাবা**
494
+ ৩. **প্ৰতিটো ধাপৰ ব্যাখ্যা দিবা**
495
+ ৪. **সহজ পদ্ধতিৰে বুজাবা**
496
+ ৫. **পৰীক্ষাৰ বাবে গুৰুত্বপূৰ্ণ সূত্ৰবোৰ পৃথকৈ দেখুৱাবা**
497
+ ৬. **সকলো গাণিতিক সমীকৰণ আৰু সূতৰবোৰ `$` চিহ্নৰ মাজত লিখিবা, আলোচনাৰ বাহিৰত পৃথক লাইনত দেখুৱাবা।**
498
+
499
+ **গণিতৰ সূত্ৰৰ উদাহৰণ (LaTeX ফৰ্মেটত):**
500
+ - দ্বিঘাত সমীকৰণ: $ax^2 + bx + c = 0$
501
+ - বৃত্তৰ কালি: $A = \\pi r^2$
502
+ - সম্ভাৱিতা: $P(E) = \\frac{{n(E)}}{{n(S)}}$
503
+ - পাইথাগোৰাছৰ উপপাদ্য: $a^2 + b^2 = c^2$
504
+
505
+ **বক্তব্য শৈলী:**
506
+ "চিন্তা নকৰিব, এই গণিতৰ সমস্যাটো সহজ।"
507
+ "ধাপে ধাপে শিকো আহক..."
508
+ "এই সূত্ৰটো মনত ৰাখিব - পৰীক্ষাত আহিব পাৰে!" """,
509
+
510
+ "guidance": "সমীকৰণ, সূত্ৰ আৰু গাণিতিক প্ৰক্ৰিয়া LaTeX ফৰ্মেটত দেখুৱাব লাগে।"
511
+ },
512
+
513
+ "🔬 বিজ্ঞান (Science)": {
514
+ "base_prompt": """তুমি এজন বিজ্ঞান শিক্ষক। SEBA দশম শ্ৰেণীৰ বিজ্ঞানৰ {chapter_name} অধ্যায়ৰ স��লো বৈজ্ঞানিক ধাৰণা, প্ৰক্ৰয়া, আৰু নীতি তুমি জানা।
515
+
516
+ **বিজ্ঞানৰ বিশেষ নিৰ্দেশনা:**
517
+ ১. **বৈজ্ঞানিক প্ৰক্ৰিয়া ধাপে ধাপে বুজাবা**
518
+ ২. **ৰাসায়নিক সমীকৰণ সঠিকভাৱে দিবা**
519
+ ৩. **জীৱবিজ্ঞানৰ চিত্ৰ/ৰেখাচিত্ৰৰ বৰ্ণনা দিবা**
520
+ ৪. **পদাৰ্থবিজ্ঞানৰ সূত্ৰ LaTeX ফৰ্মেটত দিবা**
521
+
522
+ **ৰাসায়নিক উদাহৰণ:**
523
+ $2H_2 + O_2 \\rightarrow 2H_2O$
524
+
525
+ **পদাৰ্থবিজ্ঞান সূত্ৰ:**
526
+ $F = ma$, $v = u + at$
527
+
528
+ **বক্তব্য শৈলী:**
529
+ "এই বৈজ্ঞানিক ধাৰণাটো বুজোৱাৰ বাবে এটা সাধাৰণ উদাহৰণ চাওঁ..."
530
+ "প্ৰকতিৰ এই ৰহস্যবোৰ মন কৰিছিল নেকি?" """,
531
+
532
+ "guidance": "ৰাসায়নিক সমীকৰণ আৰু পদাৰ্থবিজ্ঞানৰ সূত্ৰ LaTeX ফৰ্মেটত দিব লাগে।"
533
+ },
534
+
535
+ "🌍 সমাজ বিজ্ঞান (Social Science)": {
536
+ "base_prompt": """তুমি এজন সমাজ বিজ্ঞান শিক্ষক। SEBA দশম শ্ৰেণীৰ {chapter_name} অধ্যায়ৰ ঐতিহাসিক ঘটনা, ভৌগোলিক ধাৰণা, অৰ্থনৈতিক নীতি, আৰু ৰাজনৈতিক গঠন তুমি জানা।
537
+
538
+ **সমাজ বিজ্ঞানৰ বিশেষ নিৰ্দেশনা:**
539
+ ১. **সহজ অসমীয়া ভাষা ব্যৱহাৰ কৰিবা**
540
+ ২. **প্ৰশ্ন অনুসৰি উত্তৰ দিবা**""",
541
+
542
+ "guidance": "তথ্য আৰু বিশ্লেষণ স্পষ্টকৈ দিব লাগে।"
543
+ },
544
+
545
+ "📖 ইংৰাজী (English)": {
546
+ "base_prompt": """তুমি এজন ইংৰাজী শিক্ষক। SEBA দশম শ্ৰেণীৰ {chapter_name} পাঠটোৰ সকলো সাহিত্যিক উপাদান, ব্যাকৰণ, আৰু ভাষা কৌশল তুমি জানা।
547
+
548
+ **ইংৰাজীৰ বিশেষ নিৰ্দেশনা:**
549
+ ১. Answer in English with Assamese translation""",
550
+
551
+ "guidance": "ইংৰাজী বাক্যৰ সৈতে অসমীয়া ব্যাখ্যা দিব লাগে।"
552
+ },
553
+
554
+ "📜 অসমীয়া (Assamese)": {
555
+ "base_prompt": """তুমি এজন অসমীয়া সাহিত্য শিক্ষক। SEBA দশম শ্ৰেণীৰ {chapter_name} পাঠটোৰ সাহিত্যিক মুল্য, ভাষা বৈশিষ্ট্য, আৰু সাংস্কৃতিক প্ৰসংগ তুমি জানা।
556
+
557
+ **অসমীয়াৰ বিশেষ নিৰ্দেশনা:**
558
+ ১. **সাহিত্যিক বিশ্লেষণ অসমীয়াত দিবা**
559
+ ২. **প্ৰশ্ন অনুসৰি উত্তৰ দিবা**""",
560
+
561
+ "guidance": "অসমীয়া ভাষাৰ সৌন্দৰ্য্য আৰু গভীৰতা দেখুৱাব লাগে।"
562
+ },
563
+
564
+ "📘 হিন্দী (Hindi)": {
565
+ "base_prompt": """तुम एक हिंदी शिक्षक हो। SEBA दशम श्रेणी के {chapter_name} पाठ के सभी साहित्यिक तत्व, व्याकरण, और भाषा कौशल तुम जानते हो।
566
+
567
+ **हिंदी के विशेष निर्देश:**
568
+ १. **साहित्यिक विश्लेषण हिंदी में देना, साथ असमिया व्याख्या देना**
569
+ २. **प्रश्न के अनुसार उत्तर देना**""",
570
+
571
+ "guidance": "हिंदी वाक्य के साथ असमिया व्याख्या देना"
572
+ }
573
+ }
574
+
575
+ # ===============================
576
+ # HELPER FUNCTIONS
577
+ # ===============================
578
+ def create_cache_key(question, subject, chapter_name):
579
+ """Create a unique cache key for the question"""
580
+ # Normalize the question for better caching
581
+ normalized_question = question.strip().lower()
582
+ # Remove extra whitespace and special characters
583
+ normalized_question = re.sub(r'\s+', ' ', normalized_question)
584
+ normalized_question = normalized_question[:200] # Limit length
585
+
586
+ # Create a hash-based key for better performance
587
+ key_string = f"{subject}|{chapter_name}|{normalized_question}"
588
+ cache_key = hashlib.md5(key_string.encode()).hexdigest()
589
+
590
+ return cache_key
591
+
592
+ def get_question_guidance(question, subject, chapter_name):
593
+ question_lower = question.lower()
594
+
595
+ simple_keywords = [
596
+ "সংজ্ঞা", "কি", "কাক কয়", "মানে", "definition", "what is",
597
+ "নাম", "কেইটা", "কিমান", "count", "number", "কি নাম", "কাক বোলে"
598
+ ]
599
+
600
+ moderate_keywords = [
601
+ "কেনেকৈ", "কেনেকুৱা", "কিয়", "বুজাই দিয়ক", "explain", "how",
602
+ "why", "difference", "পাৰ্থক্য", "উদাহৰণ", "example", "সমাধান",
603
+ "solve", "কোনবোৰ", "তুলনা", "compare", "সাদৃশ্য", "similarity"
604
+ ]
605
+
606
+ complex_keywords = [
607
+ "বিশ্লেষণ", "আলোচনা", "মূল্যায়ন", "বৰ্ণনা", "discuss",
608
+ "analyze", "evaluate", "describe", "প্ৰমাণ", "prove",
609
+ "সমাধান কৰি দেখুৱাওক", "solve and show", "step by step",
610
+ "ধাপে ধাপে", "সম্পূৰ্ণ", "সম্পূৰ্ণ বিৱৰণ", "full explanation",
611
+ "সবিশেষ", "in detail", "detailed", "সবিস্তাৰে"
612
+ ]
613
+
614
+ guidance_text = ""
615
+
616
+ if "📐 গণিত" in subject:
617
+ guidance_text = "গণিতৰ সমস্যাৰ বাবে ধাপে ধাপে সমাধান দিব লাগে। "
618
+ elif "🔬 বিজ্ঞান" in subject:
619
+ guidance_text = "বিজ্ঞানৰ উত্তৰ বৈজ্ঞানিকভাৱে সঠিক হ'ব লাগে। "
620
+ elif "🌍 সমাজ বিজ্ঞান" in subject:
621
+ guidance_text = "তথ্য সঠিক আৰু বিশ্লেষণাত্মক হ'ব লাগে। "
622
+
623
+ if any(keyword in question_lower for keyword in complex_keywords):
624
+ return f"{guidance_text} প্ৰশ্নটো জটিল, গতিকে বিশদ উত্তৰ দিবা।"
625
+ elif any(keyword in question_lower for keyword in moderate_keywords):
626
+ return f"{guidance_text} প্ৰশ্নটো মধ্যমীয়া, গতিকে সম্পূৰ্ণ উত্তৰ দিবা।"
627
+ elif any(keyword in question_lower for keyword in simple_keywords):
628
+ return f"{guidance_text} প্ৰশ্নটো সৰল, গতিকে সংক্ষিপ্ত উত্তৰ দিবা।"
629
+ else:
630
+ return f"{guidance_text} প্ৰশ্নৰ প্ৰকৃতি অনুসৰি উত্তৰ দিবা।"
631
+
632
+ def get_subject_prompt(subject, chapter_name, question):
633
+ if subject not in SUBJECT_PROMPTS:
634
+ subject = "📐 গণিত (Mathematics)"
635
+
636
+ prompt_template = SUBJECT_PROMPTS[subject]
637
+ base_prompt = prompt_template["base_prompt"].format(chapter_name=chapter_name)
638
+ guidance = prompt_template["guidance"]
639
+
640
+ if subject == "📐 গণিত (Mathematics)" or subject == "🔬 বিজ্ঞান (Science)":
641
+ latex_instruction = "\n\n**গুৰুত্বপূৰ্ণ**: সকলো গাণিতিক সূত্ৰ, সমীকৰণ LaTeX ফৰ্মেটত দিবা ($ চিহ্নৰ মাজত)।"
642
+ else:
643
+ latex_instruction = ""
644
+
645
+ question_guidance = get_question_guidance(question, subject, chapter_name)
646
+
647
+ full_prompt = f"""{base_prompt}
648
+
649
+ {guidance}{latex_instruction}
650
+
651
+ **উত্তৰৰ নিৰ্দেশনা:**
652
+ {question_guidance}
653
+ **উত্তৰ যিমান দৰকাৰী সিমান দীঘল হ'ব লাগে।**
654
+
655
+ **ছাত্ৰক মাতি লওঁ:**
656
+ "বন্ধু, এইটো এনেদৰে বুজিব লাগে..."
657
+ "চিন্তা নকৰিব, এইটো সহজ..."
658
+
659
+ এতিয়া এই প্ৰশ্নটোৰ উত্তৰ দিয়া: {question}"""
660
+
661
+ return full_prompt
662
+
663
+ # ===============================
664
+ # STREAMLIT STREAMING RESPONSE FUNCTION
665
+ # ===============================
666
+ def stream_deepseek_response(prompt, question, subject, chapter_name):
667
+ headers = {
668
+ "Authorization": f"Bearer {api_key}",
669
+ "Content-Type": "application/json"
670
+ }
671
+
672
+ payload = {
673
+ "model": "deepseek-chat",
674
+ "messages": [
675
+ {"role": "system", "content": "তুমি এজন বিশেষজ্ঞ SEBA দশম শ্ৰেণীৰ শিক্ষক।"},
676
+ {"role": "user", "content": prompt}
677
+ ],
678
+ "temperature": 0.3,
679
+ "stream": True
680
+ }
681
+
682
+ try:
683
+ # Make streaming request
684
+ response = requests.post(
685
+ "https://api.deepseek.com/v1/chat/completions",
686
+ headers=headers,
687
+ json=payload,
688
+ stream=True,
689
+ timeout=180
690
+ )
691
+
692
+ if response.status_code == 200:
693
+ full_response = ""
694
+ tokens_used = 0
695
+
696
+ # Create a placeholder for streaming text
697
+ streaming_placeholder = st.empty()
698
+
699
+ # Process streaming response
700
+ for line in response.iter_lines():
701
+ if line:
702
+ line = line.decode('utf-8')
703
+ if line.startswith('data: '):
704
+ data = line[6:] # Remove 'data: ' prefix
705
+ if data == '[DONE]':
706
+ break
707
+
708
+ try:
709
+ import json
710
+ chunk = json.loads(data)
711
+ if 'choices' in chunk and len(chunk['choices']) > 0:
712
+ delta = chunk['choices'][0].get('delta', {})
713
+ if 'content' in delta:
714
+ content = delta['content']
715
+ full_response += content
716
+
717
+ # Update streaming display - Streamlit will auto-render LaTeX
718
+ streaming_placeholder.markdown(
719
+ f"{full_response}<span style='animation: cursor-blink 1s infinite;'>▋</span>",
720
+ unsafe_allow_html=True
721
+ )
722
+
723
+ # Track tokens
724
+ if 'usage' in chunk:
725
+ tokens_used = chunk['usage'].get('total_tokens', 0)
726
+ except json.JSONDecodeError:
727
+ continue
728
+
729
+ # Clear streaming cursor after completion
730
+ streaming_placeholder.empty()
731
+
732
+ # Render the final answer - Streamlit will handle LaTeX automatically
733
+ st.markdown(full_response)
734
+
735
+ # Store the complete response
736
+ st.session_state.last_answer = full_response
737
+ st.session_state.tokens_used = tokens_used
738
+
739
+ # Save to cache using manager
740
+ cache_key = create_cache_key(question, subject, chapter_name)
741
+ st.session_state.cache_manager.set(cache_key, {
742
+ 'answer': full_response,
743
+ 'tokens': tokens_used,
744
+ 'subject': subject,
745
+ 'chapter': chapter_name,
746
+ 'question': question[:200]
747
+ })
748
+
749
+ # Add to history
750
+ history_entry = {
751
+ 'subject': subject,
752
+ 'chapter': chapter_name,
753
+ 'question': question[:100],
754
+ 'timestamp': datetime.now().strftime("%H:%M"),
755
+ 'tokens': tokens_used,
756
+ 'cached': False # Not cached (new response)
757
+ }
758
+ st.session_state.history.append(history_entry)
759
+
760
+ else:
761
+ st.error(f"API ত্ৰুটি {response.status_code}: {response.text}")
762
+
763
+ except Exception as e:
764
+ st.error(f"সংযোগ ত্ৰুটি: {str(e)}")
765
+
766
+ # ===============================
767
+ # INITIALIZE SESSION STATE
768
+ # ===============================
769
+ if 'history' not in st.session_state:
770
+ st.session_state.history = []
771
+ if 'current_subject' not in st.session_state:
772
+ st.session_state.current_subject = "📐 গণিত (Mathematics)"
773
+ if 'current_chapter' not in st.session_state:
774
+ st.session_state.current_chapter = "অধ্যায় ১"
775
+ if 'processing' not in st.session_state:
776
+ st.session_state.processing = False
777
+ if 'last_answer' not in st.session_state:
778
+ st.session_state.last_answer = None
779
+ if 'question_text' not in st.session_state:
780
+ st.session_state.question_text = ""
781
+ if 'streaming_answer' not in st.session_state:
782
+ st.session_state.streaming_answer = ""
783
+ if 'tokens_used' not in st.session_state:
784
+ st.session_state.tokens_used = 0
785
+ if 'cache_manager' not in st.session_state:
786
+ st.session_state.cache_manager = HybridCache(ttl_hours=24, max_entries=100)
787
+ if 'show_cached_answer' not in st.session_state:
788
+ st.session_state.show_cached_answer = False
789
+ if 'cached_answer_data' not in st.session_state:
790
+ st.session_state.cached_answer_data = None
791
+ if 'current_cache_key' not in st.session_state:
792
+ st.session_state.current_cache_key = None
793
+
794
+ # ===============================
795
+ # HEADER SECTION
796
+ # ===============================
797
+ st.markdown("""
798
+ <div class="header-container">
799
+ <div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;">
800
+ <div style="font-size: 2rem;">🎓</div>
801
+ <div>
802
+ <h1 class="assamese-title">
803
+ নমস্কাৰ! মই আপোনাৰ দশম শ্ৰেণীৰ AI শিক্ষক
804
+ </h1>
805
+ <p class="assamese-text">
806
+ <span class="assamese-highlight">SEBAৰ সকলো বিষয় মই জানো</span> – গণিত, বিজ্ঞান, সমাজ বিজ্ঞান, ইংৰাজী, অসমীয়া, হিন্দী ইত্যাদি।
807
+ </p>
808
+ </div>
809
+ </div>
810
+ </div>
811
+ """, unsafe_allow_html=True)
812
+
813
+ # ===============================
814
+ # CONTROL PANEL SECTION
815
+ # ===============================
816
+ st.markdown('<div class="control-panel">', unsafe_allow_html=True)
817
+
818
+ control_col1, control_col2 = st.columns(2)
819
+ with control_col1:
820
+ st.markdown("#### 📚 বিষয় বাছনি কৰক")
821
+ subject_list = list(SEBA_CURRICULUM.keys())
822
+ current_subject = st.session_state.current_subject
823
+ current_index = subject_list.index(current_subject) if current_subject in subject_list else 0
824
+
825
+ selected_subject = st.selectbox(
826
+ "আপুনি কোনটো বিষয় শিকিব বিচাৰে?",
827
+ subject_list,
828
+ index=current_index,
829
+ key="subject_selector",
830
+ label_visibility="collapsed"
831
+ )
832
+
833
+ if selected_subject != st.session_state.current_subject:
834
+ st.session_state.current_subject = selected_subject
835
+ chapters = SEBA_CURRICULUM[selected_subject]
836
+ st.session_state.current_chapter = list(chapters.keys())[0]
837
+
838
+ with control_col2:
839
+ st.markdown("#### 📖 অধ্যায় বাছনি কৰক")
840
+ chapters = SEBA_CURRICULUM[selected_subject]
841
+
842
+ chapter_options = []
843
+ chapter_display_map = {}
844
+ for chap_num, chap_name in chapters.items():
845
+ display_text = f"{chap_num}: {chap_name}"
846
+ chapter_options.append(display_text)
847
+ chapter_display_map[display_text] = chap_num
848
+
849
+ current_chapter = st.session_state.current_chapter
850
+ current_chap_display = next((disp for disp, num in chapter_display_map.items() if num == current_chapter), chapter_options[0])
851
+ current_chap_index = chapter_options.index(current_chap_display) if current_chap_display in chapter_options else 0
852
+
853
+ selected_chapter_display = st.selectbox(
854
+ "কোন অধ্যায়ৰ পৰা প্ৰশ্ন সুধিব?",
855
+ chapter_options,
856
+ index=current_chap_index,
857
+ key="chapter_selector",
858
+ label_visibility="collapsed"
859
+ )
860
+
861
+ selected_chapter_key = chapter_display_map[selected_chapter_display]
862
+ if selected_chapter_key != st.session_state.current_chapter:
863
+ st.session_state.current_chapter = selected_chapter_key
864
+
865
+ st.markdown('</div>', unsafe_allow_html=True)
866
+
867
+ # ===============================
868
+ # CURRENT SELECTION INFO
869
+ # ===============================
870
+ current_chapter_name = chapters[selected_chapter_key]
871
+ st.info(f"""
872
+ **📚 বৰ্তমানৰ বিষয়:** {selected_subject}
873
+ **📖 বৰ্তমানৰ অধ্যায়:** {current_chapter_name}
874
+ """)
875
+
876
+ # ===============================
877
+ # SAMPLE QUESTIONS SECTION (SKIPPED - ADD FROM YOUR SIDE)
878
+ # ===============================
879
+ # [Add your sample questions code here]
880
+
881
+ # ===============================
882
+ # QUESTION INPUT AREA
883
+ # ===============================
884
+ st.markdown("---")
885
+ st.markdown("#### ✍️ আপোনাৰ প্ৰশ্নটো ইয়াত লিখক")
886
+
887
+ question = st.text_area(
888
+ "আপোনাৰ প্ৰশ্নটো ইয়াত লিখক:",
889
+ value=st.session_state.question_text,
890
+ height=100,
891
+ placeholder=f"উদাহৰণ: '{current_chapter_name}' অধ্যায়টো মোৰ বাবে বুজাই দিয়ক...",
892
+ key="question_input",
893
+ label_visibility="collapsed"
894
+ )
895
+
896
+ if question != st.session_state.question_text:
897
+ st.session_state.question_text = question
898
+
899
+ # Show API key status
900
+ if not api_key:
901
+ st.error("""
902
+ ⚠️ **API কি ছেট আপ কৰক:**
903
+
904
+ **Hugging Face Spaces:**
905
+ ১. Space Settings → Repository secrets
906
+ ২. `DEEPSEEK_API_KEY` যোগ কৰক
907
+ ৩. আপোনাৰ DeepSeek API কি দিয়ক
908
+
909
+ **স্থানীয়ভাবে:**
910
+ ```bash
911
+ export DEEPSEEK_API_KEY="your-api-key-here"
912
+ ```
913
+ """)
914
+
915
+ # ===============================
916
+ # CACHE CHECK AND SUBMIT BUTTON
917
+ # ===============================
918
+ submit_disabled = not (question.strip() and api_key)
919
+ col1, col2, col3 = st.columns([1, 2, 1])
920
+ with col2:
921
+ if st.button(
922
+ "🚀 উত্তৰ দিবলৈ দিয়ক!",
923
+ type="primary",
924
+ use_container_width=True,
925
+ disabled=submit_disabled
926
+ ):
927
+ if not question.strip():
928
+ st.error("❌ অনুগ্ৰহ কৰি প্ৰশ্নটো লিখক!")
929
+ elif not api_key:
930
+ st.error("❌ API কি ছেট আপ কৰক!")
931
+ else:
932
+ # Check cache first
933
+ cache_key = create_cache_key(question, selected_subject, current_chapter_name)
934
+ cached_entry = st.session_state.cache_manager.get(cache_key)
935
+
936
+ if cached_entry:
937
+ # Load from cache
938
+ st.session_state.last_answer = cached_entry['answer']
939
+ st.session_state.tokens_used = cached_entry['tokens']
940
+
941
+ # Add to history with cache flag
942
+ history_entry = {
943
+ 'subject': selected_subject,
944
+ 'chapter': current_chapter_name,
945
+ 'question': question[:100],
946
+ 'timestamp': datetime.now().strftime("%H:%M"),
947
+ 'tokens': cached_entry['tokens'],
948
+ 'cached': True
949
+ }
950
+ st.session_state.history.append(history_entry)
951
+
952
+ # Show cached answer
953
+ st.session_state.show_cached_answer = True
954
+ st.session_state.cached_answer_data = cached_entry
955
+ st.session_state.current_cache_key = cache_key
956
+ st.session_state.processing = False
957
+ else:
958
+ # Not in cache, proceed with API call
959
+ st.session_state.processing = True
960
+ st.session_state.current_cache_key = cache_key
961
+
962
+ # ===============================
963
+ # DISPLAY CACHED ANSWER
964
+ # ===============================
965
+ if hasattr(st.session_state, 'show_cached_answer') and st.session_state.show_cached_answer:
966
+ st.markdown("---")
967
+
968
+ # User question
969
+ st.markdown(f"""
970
+ <div style="margin-bottom: 1rem;">
971
+ <div style="display: flex; justify-content: flex-end; margin-bottom: 0.3rem;">
972
+ <div class="user-bubble">
973
+ <div style="font-weight: 600; margin-bottom: 0.2rem;">👤 আপুনি:</div>
974
+ <div>{question}</div>
975
+ </div>
976
+ </div>
977
+ </div>
978
+ """, unsafe_allow_html=True)
979
+
980
+ # Cached answer with indicator
981
+ cached_data = st.session_state.cached_answer_data
982
+
983
+ st.markdown(f"""
984
+ <div style="margin-bottom: 0.5rem;">
985
+ <div style="display: flex; align-items: flex-start; margin-bottom: 0.3rem;">
986
+ <div style="margin-right: 0.5rem; font-size: 1.2rem;">🤖</div>
987
+ <div style="flex: 1;">
988
+ <div class="ai-bubble">
989
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; padding-bottom: 0.5rem; border-bottom: 2px solid #4CAF50;">
990
+ <div style="display: flex; align-items: center;">
991
+ <div style="background: #4CAF50; color: white; padding: 0.2rem 0.5rem; border-radius: 8px;
992
+ font-weight: 600; font-size: 0.8rem; margin-right: 0.5rem;">
993
+ <span style="margin-right: 0.3rem;">⚡</span> Cached
994
+ </div>
995
+ <div style="font-weight: 600; color: #0d47a1; font-size: 0.9rem;">
996
+ {selected_subject} • {current_chapter_name}
997
+ </div>
998
+ </div>
999
+ <div style="font-size: 0.75rem; color: #666; background: #f1f8e9; padding: 0.2rem 0.5rem; border-radius: 4px;">
1000
+ <span style="margin-right: 0.3rem;">💾</span> Cache Hit
1001
+ </div>
1002
+ </div>
1003
+ <div style="color: #333; line-height: 1.5; font-size: 0.95rem;">
1004
+ {cached_data['answer']}
1005
+ </div>
1006
+ </div>
1007
+ </div>
1008
+ </div>
1009
+ </div>
1010
+ """, unsafe_allow_html=True)
1011
+
1012
+ # Show cache info
1013
+ with st.expander("📊 Cache Information"):
1014
+ cache_stats = st.session_state.cache_manager.get_stats()
1015
+ estimated_cost = cached_data.get('tokens', 0) * 0.0000014 # $0.14 per 100K tokens
1016
+ st.info(f"""
1017
+ **Cache Benefits:**
1018
+ - ⚡ Instant response (no API call)
1019
+ - 💰 No token cost (saved {cached_data.get('tokens', 0):,} tokens)
1020
+ - 💵 Estimated savings: ${estimated_cost:.4f}
1021
+ - 🌿 Environmentally friendly
1022
+
1023
+ **Cache Details:**
1024
+ - Cache entries: {cache_stats['total_entries']}
1025
+ - Total tokens saved: {cache_stats['total_saved_tokens']:,}
1026
+ - Cache hit count: {cached_data.get('access_count', 1)}
1027
+ - Cache TTL: {cache_stats['ttl_hours']} hours
1028
+ - Storage: {cache_stats['storage_mode']}
1029
+ """)
1030
+
1031
+ # Cache management buttons
1032
+ col1, col2 = st.columns(2)
1033
+ with col1:
1034
+ if st.button("🗑️ Clear this cached answer", type="secondary", use_container_width=True):
1035
+ if st.session_state.current_cache_key in st.session_state.cache_manager.cache:
1036
+ st.session_state.cache_manager.cache.pop(st.session_state.current_cache_key)
1037
+ st.success("Cache entry cleared!")
1038
+ st.session_state.show_cached_answer = False
1039
+ st.rerun()
1040
+
1041
+ with col2:
1042
+ if st.button("🧹 Clear all cache", type="secondary", use_container_width=True):
1043
+ st.session_state.cache_manager.clear_all()
1044
+ st.success("All cache cleared!")
1045
+ st.session_state.show_cached_answer = False
1046
+ st.rerun()
1047
+
1048
+ # Show token usage
1049
+ if cached_data.get('tokens', 0) > 0:
1050
+ st.caption(f"📊 Original token cost (saved): {cached_data['tokens']:,}")
1051
+
1052
+ # Reset flag
1053
+ st.session_state.show_cached_answer = False
1054
+ del st.session_state.cached_answer_data
1055
+ del st.session_state.current_cache_key
1056
+
1057
+ # ===============================
1058
+ # PROCESS QUESTION WITH STREAMING
1059
+ # ===============================
1060
+ if st.session_state.get('processing') and question and api_key and not st.session_state.get('show_cached_answer', False):
1061
+ st.markdown("---")
1062
+
1063
+ # User question
1064
+ st.markdown(f"""
1065
+ <div style="margin-bottom: 1rem;">
1066
+ <div style="display: flex; justify-content: flex-end; margin-bottom: 0.3rem;">
1067
+ <div class="user-bubble">
1068
+ <div style="font-weight: 600; margin-bottom: 0.2rem;">👤 আপুনি:</div>
1069
+ <div>{question}</div>
1070
+ </div>
1071
+ </div>
1072
+ </div>
1073
+ """, unsafe_allow_html=True)
1074
+
1075
+ # AI answer header (with thinking animation)
1076
+ st.markdown(f"""
1077
+ <div style="margin-bottom: 0.5rem;">
1078
+ <div style="display: flex; align-items: flex-start; margin-bottom: 0.3rem;">
1079
+ <div style="margin-right: 0.5rem; font-size: 1.2rem;">🤖</div>
1080
+ <div style="flex: 1;">
1081
+ <div class="ai-bubble">
1082
+ <div style="display: flex; align-items: center; margin-bottom: 0.5rem; padding-bottom: 0.5rem; border-bottom: 2px solid #2196F3;">
1083
+ <div style="background: #2196F3; color: white; padding: 0.2rem 0.5rem; border-radius: 8px;
1084
+ font-weight: 600; font-size: 0.8rem; margin-right: 0.5rem;">
1085
+ AI টিউটাৰ
1086
+ </div>
1087
+ <div style="font-weight: 600; color: #0d47a1; font-size: 0.9rem;">
1088
+ {selected_subject} • {current_chapter_name}
1089
+ </div>
1090
+ </div>
1091
+ <div style="color: #333; line-height: 1.5; font-size: 0.95rem; min-height: 100px;">
1092
+ """, unsafe_allow_html=True)
1093
+
1094
+ # Show thinking animation while preparing response
1095
+ thinking_placeholder = st.empty()
1096
+ thinking_placeholder.markdown("""
1097
+ <div class="progress-indicator">
1098
+ <span>উত্তৰ প্ৰস্তুত কৰি আছো...</span>
1099
+ <div class="thinking-dots">
1100
+ <span></span>
1101
+ <span></span>
1102
+ <span></span>
1103
+ </div>
1104
+ </div>
1105
+ """, unsafe_allow_html=True)
1106
+
1107
+ # Get the prompt and stream the response
1108
+ system_prompt = get_subject_prompt(selected_subject, current_chapter_name, question)
1109
+
1110
+ # Clear thinking animation and start streaming
1111
+ thinking_placeholder.empty()
1112
+
1113
+ # Stream the response
1114
+ stream_deepseek_response(system_prompt, question, selected_subject, current_chapter_name)
1115
+
1116
+ # Close the AI bubble div
1117
+ st.markdown("""
1118
+ </div>
1119
+ </div>
1120
+ </div>
1121
+ </div>
1122
+ </div>
1123
+ """, unsafe_allow_html=True)
1124
+
1125
+ # Show token usage
1126
+ if st.session_state.tokens_used > 0:
1127
+ st.caption(f"📊 ট'কেন ব্যৱহৃত: {st.session_state.tokens_used:,}")
1128
+
1129
+ st.session_state.processing = False
1130
+
1131
+ # ===============================
1132
+ # HISTORY
1133
+ # ===============================
1134
+ if st.session_state.history:
1135
+ st.markdown("---")
1136
+ st.markdown("#### 📜 আজিৰ প্ৰশ্নাৱলী")
1137
+
1138
+ for i, item in enumerate(reversed(st.session_state.history[-3:]), 1):
1139
+ cache_indicator = " ⚡" if item.get('cached') else ""
1140
+ with st.expander(f"প্ৰশ্ন {i}: {item['question']} ({item['timestamp']}{cache_indicator})"):
1141
+ st.write(f"**বিষয়:** {item['subject']}")
1142
+ st.write(f"**অধ্যায়:** {item['chapter']}")
1143
+ st.write(f"**ট'কেন:** {item.get('tokens', 0):,}")
1144
+ if item.get('cached'):
1145
+ st.caption("⚡ This answer was served from cache")
1146
+
1147
+ # ===============================
1148
+ # CACHE STATISTICS SIDEBAR
1149
+ # ===============================
1150
+ with st.sidebar:
1151
+ st.markdown("---")
1152
+ st.markdown("#### 💾 Cache Statistics")
1153
+
1154
+ cache_stats = st.session_state.cache_manager.get_stats()
1155
+
1156
+ if cache_stats['total_entries'] > 0:
1157
+ # Cache info
1158
+ st.caption(f"⚡ **Storage:** {cache_stats['storage_mode']}")
1159
+
1160
+ # Cache stats metrics
1161
+ col1, col2 = st.columns(2)
1162
+ with col1:
1163
+ st.metric("Entries", cache_stats['total_entries'])
1164
+ st.metric("Avg Age", f"{cache_stats['average_age_hours']}h")
1165
+
1166
+ with col2:
1167
+ st.metric("Tokens Saved", f"{cache_stats['total_saved_tokens']:,}")
1168
+ st.metric("TTL", f"{cache_stats['ttl_hours']}h")
1169
+
1170
+ # Cost savings
1171
+ estimated_cost = cache_stats['total_saved_tokens'] * 0.0000014 # $0.14 per 100K tokens
1172
+ st.metric("💰 Cost Saved", f"${estimated_cost:.4f}")
1173
+
1174
+ # Cache management
1175
+ st.markdown("#### 🛠️ Cache Management")
1176
+
1177
+ col1, col2 = st.columns(2)
1178
+ with col1:
1179
+ if st.button("🗑️ Clear Expired", use_container_width=True):
1180
+ cleared = st.session_state.cache_manager.clear_expired()
1181
+ if cleared > 0:
1182
+ st.success(f"Cleared {cleared} expired entries!")
1183
+ else:
1184
+ st.info("No expired entries found")
1185
+ st.rerun()
1186
+
1187
+ with col2:
1188
+ if st.button("🧹 Clear All", use_container_width=True):
1189
+ st.session_state.cache_manager.clear_all()
1190
+ st.success("All cache cleared!")
1191
+ st.rerun()
1192
+
1193
+ # View recent cached questions
1194
+ with st.expander("📋 Recent Cached Questions"):
1195
+ recent_entries = list(st.session_state.cache_manager.cache.items())[:5]
1196
+ for key, entry in recent_entries:
1197
+ question_text = entry.get('question', 'N/A')[:40] + "..."
1198
+ age_hours = round((datetime.now() - datetime.fromisoformat(entry.get('created_at', datetime.now().isoformat()))).seconds / 3600, 1)
1199
+ st.caption(f"• {question_text}")
1200
+ st.write(f" 📚 {entry.get('subject', 'N/A').split()[0]} • 🔢 {entry.get('access_count', 1)}x • ⏳ {age_hours}h ago")
1201
+ else:
1202
+ st.info("Cache is empty. Ask some questions to build cache!")
1203
+
1204
+ # Cache performance note
1205
+ st.markdown("---")
1206
+ st.caption("""
1207
+ **💡 Cache Performance:**
1208
+ - Cache persists for 24 hours
1209
+ - In-memory storage (no file access)
1210
+ - Automatic cleanup of old entries
1211
+ - Works within browser session
1212
+ """)
1213
+
1214
+ # ===============================
1215
+ # FOOTER
1216
+ # ===============================
1217
+ st.markdown("---")
1218
+ st.markdown("""
1219
+ <div style="text-align: center; padding: 0.5rem;">
1220
+ <h3 style="color: #0d47a1; margin-bottom: 0.5rem;">
1221
+ 🎓 আপোনাৰ সফলতাৰ বাবে মই সদায় আছো!
1222
+ </h3>
1223
+ </div>
1224
+ """, unsafe_allow_html=True)
1225
+
1226
+ st.markdown("""
1227
+ <div style="text-align: center; padding: 0.5rem; margin-top: 1rem; color: #1976D2; font-size: 0.8rem;">
1228
+ <p style="margin: 0;">© 2025 Jajabor AI. All rights reserved.</p>
1229
+ </div>
1230
+ """, unsafe_allow_html=True)