Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -35,8 +35,8 @@ user_db = load_db()
|
|
| 35 |
current_key_index = 0
|
| 36 |
app = Flask(__name__)
|
| 37 |
|
| 38 |
-
# --- 🧠 SYSTEM INSTRUCTION ---
|
| 39 |
-
|
| 40 |
ROLE: You are "Student's AI", a professional academic tutor.
|
| 41 |
RULES:
|
| 42 |
1. **MATH:** Use LaTeX for formulas ($$ ... $$).
|
|
@@ -68,7 +68,7 @@ def process_image(image_data):
|
|
| 68 |
return Image.open(io.BytesIO(image_bytes))
|
| 69 |
except: return None
|
| 70 |
|
| 71 |
-
def generate_with_retry(prompt, image_data=None, file_text=None, history_messages=[]):
|
| 72 |
global current_key_index
|
| 73 |
if not API_KEYS: return "🚨 API Keys Missing."
|
| 74 |
|
|
@@ -78,8 +78,15 @@ def generate_with_retry(prompt, image_data=None, file_text=None, history_message
|
|
| 78 |
formatted_history.append({"role": role, "parts": [m["content"]]})
|
| 79 |
|
| 80 |
current_parts = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
if file_text: current_parts.append(f"analyzing file:\n{file_text}\n\n")
|
| 82 |
-
current_parts.append(
|
|
|
|
| 83 |
if image_data:
|
| 84 |
img = process_image(image_data)
|
| 85 |
if img: current_parts.append(img)
|
|
@@ -94,13 +101,13 @@ def generate_with_retry(prompt, image_data=None, file_text=None, history_message
|
|
| 94 |
|
| 95 |
try:
|
| 96 |
genai.configure(api_key=key)
|
| 97 |
-
model = genai.GenerativeModel(model_name=model_name, system_instruction=
|
| 98 |
|
| 99 |
if image_data or file_text:
|
| 100 |
response = model.generate_content(current_parts)
|
| 101 |
else:
|
| 102 |
chat = model.start_chat(history=formatted_history)
|
| 103 |
-
response = chat.send_message(
|
| 104 |
return response.text
|
| 105 |
except Exception as e:
|
| 106 |
current_key_index = (current_key_index + 1) % len(API_KEYS)
|
|
@@ -108,7 +115,7 @@ def generate_with_retry(prompt, image_data=None, file_text=None, history_message
|
|
| 108 |
|
| 109 |
return "⚠️ System Busy. Please try again."
|
| 110 |
|
| 111 |
-
# --- UI TEMPLATE
|
| 112 |
HTML_TEMPLATE = """
|
| 113 |
<!DOCTYPE html>
|
| 114 |
<html lang="en">
|
|
@@ -149,10 +156,10 @@ HTML_TEMPLATE = """
|
|
| 149 |
-webkit-user-select: none; user-select: none;
|
| 150 |
}
|
| 151 |
|
| 152 |
-
textarea, input { -webkit-user-select: text !important; user-select: text !important; }
|
| 153 |
.user-content, .ai-content, code, pre { -webkit-user-select: none !important; user-select: none !important; }
|
| 154 |
|
| 155 |
-
/* --- APP CONTAINER
|
| 156 |
#app-container {
|
| 157 |
display: flex; flex-direction: column;
|
| 158 |
height: 100dvh; width: 100%;
|
|
@@ -176,6 +183,7 @@ HTML_TEMPLATE = """
|
|
| 176 |
}
|
| 177 |
.menu-btn:active { transform: scale(0.95); background: #222; }
|
| 178 |
.app-title { font-size: 24px; font-weight: 800; letter-spacing: -0.5px; color: #fff; }
|
|
|
|
| 179 |
|
| 180 |
/* --- SIDEBAR ANIMATION --- */
|
| 181 |
#sidebar {
|
|
@@ -234,7 +242,7 @@ HTML_TEMPLATE = """
|
|
| 234 |
/* --- LOCKED INTRO TEXT --- */
|
| 235 |
#intro-container {
|
| 236 |
position: absolute;
|
| 237 |
-
top: 140px;
|
| 238 |
left: 50%;
|
| 239 |
transform: translateX(-50%);
|
| 240 |
width: 90%; max-width: 600px;
|
|
@@ -278,23 +286,25 @@ HTML_TEMPLATE = """
|
|
| 278 |
.preview-img { width: 100%; height: 100%; object-fit: cover; }
|
| 279 |
.remove-preview { position: absolute; top: -8px; right: -8px; background: red; color: white; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; cursor: pointer; border: none; display: flex; align-items: center; justify-content: center; }
|
| 280 |
|
| 281 |
-
/* --- LOGIN
|
| 282 |
-
|
| 283 |
position: fixed; inset: 0; background: #000; z-index: 2000;
|
| 284 |
-
display: flex;
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
transition: opacity 0.8s ease; opacity: 1; pointer-events: auto;
|
| 290 |
-
}
|
| 291 |
-
#login-overlay.hidden { opacity: 0; pointer-events: none; }
|
| 292 |
|
| 293 |
.login-box {
|
| 294 |
width: 90%; max-width: 350px; text-align: center;
|
| 295 |
-
padding:
|
| 296 |
-
/* No auto margins */
|
| 297 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
#image-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 3000; display: none; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s; }
|
| 300 |
#image-modal img { max-width: 95%; max-height: 90%; border-radius: 8px; box-shadow: 0 0 20px rgba(0,0,0,0.8); }
|
|
@@ -303,11 +313,32 @@ HTML_TEMPLATE = """
|
|
| 303 |
</head>
|
| 304 |
<body>
|
| 305 |
|
| 306 |
-
<div id="login-overlay">
|
| 307 |
<div class="login-box">
|
| 308 |
<h1 class="app-title" style="margin-bottom:10px;">Student's AI</h1>
|
| 309 |
-
<input type="text" id="username-input" placeholder="Your Name"
|
| 310 |
-
<button onclick="handleLogin()"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
</div>
|
| 312 |
</div>
|
| 313 |
|
|
@@ -333,7 +364,7 @@ HTML_TEMPLATE = """
|
|
| 333 |
<header>
|
| 334 |
<div class="menu-btn" onclick="toggleSidebar()"><i class="fas fa-bars"></i></div>
|
| 335 |
<span class="app-title">Student's AI</span>
|
| 336 |
-
<div
|
| 337 |
</header>
|
| 338 |
|
| 339 |
<div id="chat-box"></div>
|
|
@@ -361,22 +392,34 @@ HTML_TEMPLATE = """
|
|
| 361 |
<script>
|
| 362 |
let currentUser = null;
|
| 363 |
let currentChatId = null;
|
|
|
|
| 364 |
let currentAttachment = { type: null, data: null, name: null };
|
| 365 |
let longPressTimer;
|
| 366 |
|
| 367 |
// --- FIXED INTRO: LOCKED CONTAINER ---
|
| 368 |
function getIntroHtml(name) {
|
| 369 |
-
return `<div id="intro-container"><div class="msg ai-msg"><div class="ai-content"><h1>Hi ${name},</h1><p>Ready to master
|
| 370 |
}
|
| 371 |
|
| 372 |
-
// --- AUTH
|
| 373 |
function checkLogin() {
|
| 374 |
try {
|
| 375 |
-
const
|
| 376 |
-
|
| 377 |
-
|
|
|
|
|
|
|
| 378 |
document.getElementById("login-overlay").classList.add('hidden');
|
| 379 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
}
|
| 381 |
} catch(e) { console.log("Storage access denied"); }
|
| 382 |
}
|
|
@@ -387,27 +430,58 @@ HTML_TEMPLATE = """
|
|
| 387 |
if(name) {
|
| 388 |
try { localStorage.setItem("student_ai_user", name); } catch(e){}
|
| 389 |
currentUser = name;
|
| 390 |
-
//
|
| 391 |
-
|
| 392 |
-
overlay.classList.
|
| 393 |
-
|
| 394 |
-
showApp();
|
| 395 |
} else {
|
| 396 |
input.style.border = "1px solid red";
|
| 397 |
setTimeout(() => input.style.border = "1px solid #333", 2000);
|
| 398 |
}
|
| 399 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
function handleLogout() {
|
| 401 |
-
try {
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
setTimeout(() => {
|
| 407 |
-
document.getElementById('chat-box').innerHTML = "";
|
| 408 |
-
currentChatId = null;
|
| 409 |
-
document.getElementById("username-input").value = "";
|
| 410 |
-
}, 500);
|
| 411 |
}
|
| 412 |
|
| 413 |
function showApp() {
|
|
@@ -500,7 +574,14 @@ HTML_TEMPLATE = """
|
|
| 500 |
}
|
| 501 |
const res = await fetch('/chat', {
|
| 502 |
method: 'POST', headers: {'Content-Type': 'application/json'},
|
| 503 |
-
body: JSON.stringify({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
});
|
| 505 |
const data = await res.json();
|
| 506 |
|
|
@@ -655,13 +736,36 @@ HTML_TEMPLATE = """
|
|
| 655 |
box.scrollTop = box.scrollHeight;
|
| 656 |
}
|
| 657 |
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
"""
|
| 663 |
|
| 664 |
-
# --- BACKEND ROUTES ---
|
| 665 |
@app.route("/", methods=["GET"])
|
| 666 |
def home(): return render_template_string(HTML_TEMPLATE)
|
| 667 |
|
|
@@ -708,12 +812,16 @@ def chat():
|
|
| 708 |
u, cid, msg = d.get("username"), d.get("chat_id"), d.get("message")
|
| 709 |
img_data = d.get("image")
|
| 710 |
file_text = d.get("file_text")
|
|
|
|
| 711 |
|
| 712 |
if u not in user_db: user_db[u] = {}
|
| 713 |
if cid not in user_db[u]: user_db[u][cid] = {"messages": []}
|
| 714 |
|
| 715 |
user_db[u][cid]["messages"].append({"role": "user", "content": msg})
|
| 716 |
-
|
|
|
|
|
|
|
|
|
|
| 717 |
user_db[u][cid]["messages"].append({"role": "model", "content": reply})
|
| 718 |
|
| 719 |
new_title = False
|
|
@@ -728,7 +836,7 @@ def chat():
|
|
| 728 |
def manifest():
|
| 729 |
data = {
|
| 730 |
"name": "Student's AI",
|
| 731 |
-
"short_name": "
|
| 732 |
"start_url": "/",
|
| 733 |
"display": "standalone",
|
| 734 |
"orientation": "portrait",
|
|
@@ -736,12 +844,12 @@ def manifest():
|
|
| 736 |
"theme_color": "#09090b",
|
| 737 |
"icons": [
|
| 738 |
{
|
| 739 |
-
"src": "https://
|
| 740 |
"sizes": "192x192",
|
| 741 |
"type": "image/png"
|
| 742 |
},
|
| 743 |
{
|
| 744 |
-
"src": "https://
|
| 745 |
"sizes": "512x512",
|
| 746 |
"type": "image/png"
|
| 747 |
}
|
|
|
|
| 35 |
current_key_index = 0
|
| 36 |
app = Flask(__name__)
|
| 37 |
|
| 38 |
+
# --- 🧠 SYSTEM INSTRUCTION (Dynamic Context) ---
|
| 39 |
+
BASE_INSTRUCTION = """
|
| 40 |
ROLE: You are "Student's AI", a professional academic tutor.
|
| 41 |
RULES:
|
| 42 |
1. **MATH:** Use LaTeX for formulas ($$ ... $$).
|
|
|
|
| 68 |
return Image.open(io.BytesIO(image_bytes))
|
| 69 |
except: return None
|
| 70 |
|
| 71 |
+
def generate_with_retry(prompt, image_data=None, file_text=None, history_messages=[], user_context=""):
|
| 72 |
global current_key_index
|
| 73 |
if not API_KEYS: return "🚨 API Keys Missing."
|
| 74 |
|
|
|
|
| 78 |
formatted_history.append({"role": role, "parts": [m["content"]]})
|
| 79 |
|
| 80 |
current_parts = []
|
| 81 |
+
|
| 82 |
+
# Add User Context to the prompt (Hidden from user)
|
| 83 |
+
full_prompt = prompt
|
| 84 |
+
if user_context:
|
| 85 |
+
full_prompt = f"[Context: Student is studying {user_context}]\nQuestion: {prompt}"
|
| 86 |
+
|
| 87 |
if file_text: current_parts.append(f"analyzing file:\n{file_text}\n\n")
|
| 88 |
+
current_parts.append(full_prompt)
|
| 89 |
+
|
| 90 |
if image_data:
|
| 91 |
img = process_image(image_data)
|
| 92 |
if img: current_parts.append(img)
|
|
|
|
| 101 |
|
| 102 |
try:
|
| 103 |
genai.configure(api_key=key)
|
| 104 |
+
model = genai.GenerativeModel(model_name=model_name, system_instruction=BASE_INSTRUCTION)
|
| 105 |
|
| 106 |
if image_data or file_text:
|
| 107 |
response = model.generate_content(current_parts)
|
| 108 |
else:
|
| 109 |
chat = model.start_chat(history=formatted_history)
|
| 110 |
+
response = chat.send_message(full_prompt)
|
| 111 |
return response.text
|
| 112 |
except Exception as e:
|
| 113 |
current_key_index = (current_key_index + 1) % len(API_KEYS)
|
|
|
|
| 115 |
|
| 116 |
return "⚠️ System Busy. Please try again."
|
| 117 |
|
| 118 |
+
# --- UI TEMPLATE ---
|
| 119 |
HTML_TEMPLATE = """
|
| 120 |
<!DOCTYPE html>
|
| 121 |
<html lang="en">
|
|
|
|
| 156 |
-webkit-user-select: none; user-select: none;
|
| 157 |
}
|
| 158 |
|
| 159 |
+
textarea, input, select { -webkit-user-select: text !important; user-select: text !important; }
|
| 160 |
.user-content, .ai-content, code, pre { -webkit-user-select: none !important; user-select: none !important; }
|
| 161 |
|
| 162 |
+
/* --- APP CONTAINER --- */
|
| 163 |
#app-container {
|
| 164 |
display: flex; flex-direction: column;
|
| 165 |
height: 100dvh; width: 100%;
|
|
|
|
| 183 |
}
|
| 184 |
.menu-btn:active { transform: scale(0.95); background: #222; }
|
| 185 |
.app-title { font-size: 24px; font-weight: 800; letter-spacing: -0.5px; color: #fff; }
|
| 186 |
+
.settings-icon { font-size: 18px; color: #aaa; cursor: pointer; }
|
| 187 |
|
| 188 |
/* --- SIDEBAR ANIMATION --- */
|
| 189 |
#sidebar {
|
|
|
|
| 242 |
/* --- LOCKED INTRO TEXT --- */
|
| 243 |
#intro-container {
|
| 244 |
position: absolute;
|
| 245 |
+
top: 140px;
|
| 246 |
left: 50%;
|
| 247 |
transform: translateX(-50%);
|
| 248 |
width: 90%; max-width: 600px;
|
|
|
|
| 286 |
.preview-img { width: 100%; height: 100%; object-fit: cover; }
|
| 287 |
.remove-preview { position: absolute; top: -8px; right: -8px; background: red; color: white; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; cursor: pointer; border: none; display: flex; align-items: center; justify-content: center; }
|
| 288 |
|
| 289 |
+
/* --- LOGIN & SELECTION OVERLAYS (LOCKED & FIXED) --- */
|
| 290 |
+
.overlay {
|
| 291 |
position: fixed; inset: 0; background: #000; z-index: 2000;
|
| 292 |
+
display: flex; align-items: flex-start; justify-content: center;
|
| 293 |
+
padding-top: 150px;
|
| 294 |
+
transition: opacity 0.5s ease; opacity: 1; pointer-events: auto;
|
| 295 |
+
}
|
| 296 |
+
.overlay.hidden { opacity: 0; pointer-events: none; }
|
|
|
|
|
|
|
|
|
|
| 297 |
|
| 298 |
.login-box {
|
| 299 |
width: 90%; max-width: 350px; text-align: center;
|
| 300 |
+
padding: 30px; border: 1px solid var(--border); border-radius: 20px; background: #0a0a0a;
|
|
|
|
| 301 |
}
|
| 302 |
+
|
| 303 |
+
/* FORM ELEMENTS */
|
| 304 |
+
.form-label { display: block; text-align: left; font-size: 12px; color: #aaa; margin-bottom: 5px; margin-top: 10px; }
|
| 305 |
+
input, select { width: 100%; padding: 12px; margin-bottom: 5px; background: #18181b; border: 1px solid #333; color: #fff; border-radius: 8px; outline: none; font-family: 'Outfit', sans-serif; font-size: 16px; }
|
| 306 |
+
|
| 307 |
+
.start-btn { width: 100%; padding: 15px; border-radius: 12px; border: none; background: #fff; font-weight: 800; cursor: pointer; font-size: 16px; margin-top: 20px; }
|
| 308 |
|
| 309 |
#image-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 3000; display: none; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s; }
|
| 310 |
#image-modal img { max-width: 95%; max-height: 90%; border-radius: 8px; box-shadow: 0 0 20px rgba(0,0,0,0.8); }
|
|
|
|
| 313 |
</head>
|
| 314 |
<body>
|
| 315 |
|
| 316 |
+
<div id="login-overlay" class="overlay">
|
| 317 |
<div class="login-box">
|
| 318 |
<h1 class="app-title" style="margin-bottom:10px;">Student's AI</h1>
|
| 319 |
+
<input type="text" id="username-input" placeholder="Your Name" onkeydown="if(event.key==='Enter') handleLogin()">
|
| 320 |
+
<button class="start-btn" onclick="handleLogin()">Next</button>
|
| 321 |
+
</div>
|
| 322 |
+
</div>
|
| 323 |
+
|
| 324 |
+
<div id="selection-overlay" class="overlay hidden">
|
| 325 |
+
<div class="login-box">
|
| 326 |
+
<h2 style="color:#fff; margin-top:0;">Customize Profile</h2>
|
| 327 |
+
|
| 328 |
+
<span class="form-label">Education Level</span>
|
| 329 |
+
<select id="edu-level" onchange="updateEduOptions()">
|
| 330 |
+
<option value="school">School (6th - 12th)</option>
|
| 331 |
+
<option value="college">College (Arts/Engg)</option>
|
| 332 |
+
</select>
|
| 333 |
+
|
| 334 |
+
<span class="form-label">Class / Year</span>
|
| 335 |
+
<select id="edu-year">
|
| 336 |
+
</select>
|
| 337 |
+
|
| 338 |
+
<span class="form-label">Subject (Optional)</span>
|
| 339 |
+
<input type="text" id="edu-subject" placeholder="Ex: Maths, Physics...">
|
| 340 |
+
|
| 341 |
+
<button class="start-btn" onclick="handleSelection()">Start Learning</button>
|
| 342 |
</div>
|
| 343 |
</div>
|
| 344 |
|
|
|
|
| 364 |
<header>
|
| 365 |
<div class="menu-btn" onclick="toggleSidebar()"><i class="fas fa-bars"></i></div>
|
| 366 |
<span class="app-title">Student's AI</span>
|
| 367 |
+
<div class="settings-icon" onclick="openSettings()"><i class="fas fa-cog"></i></div>
|
| 368 |
</header>
|
| 369 |
|
| 370 |
<div id="chat-box"></div>
|
|
|
|
| 392 |
<script>
|
| 393 |
let currentUser = null;
|
| 394 |
let currentChatId = null;
|
| 395 |
+
let userContext = "";
|
| 396 |
let currentAttachment = { type: null, data: null, name: null };
|
| 397 |
let longPressTimer;
|
| 398 |
|
| 399 |
// --- FIXED INTRO: LOCKED CONTAINER ---
|
| 400 |
function getIntroHtml(name) {
|
| 401 |
+
return `<div id="intro-container"><div class="msg ai-msg"><div class="ai-content"><h1>Hi ${name},</h1><p>Ready to master ${userContext ? userContext.split(',')[1] : "studies"}?</p></div></div></div>`;
|
| 402 |
}
|
| 403 |
|
| 404 |
+
// --- AUTH & ONBOARDING LOGIC ---
|
| 405 |
function checkLogin() {
|
| 406 |
try {
|
| 407 |
+
const storedUser = localStorage.getItem("student_ai_user");
|
| 408 |
+
const storedContext = localStorage.getItem("student_ai_context");
|
| 409 |
+
|
| 410 |
+
if (storedUser) {
|
| 411 |
+
currentUser = storedUser;
|
| 412 |
document.getElementById("login-overlay").classList.add('hidden');
|
| 413 |
+
|
| 414 |
+
if (storedContext) {
|
| 415 |
+
userContext = storedContext;
|
| 416 |
+
document.getElementById("selection-overlay").classList.add('hidden');
|
| 417 |
+
showApp();
|
| 418 |
+
} else {
|
| 419 |
+
// User exists but context missing (New Feature)
|
| 420 |
+
document.getElementById("selection-overlay").classList.remove('hidden');
|
| 421 |
+
updateEduOptions();
|
| 422 |
+
}
|
| 423 |
}
|
| 424 |
} catch(e) { console.log("Storage access denied"); }
|
| 425 |
}
|
|
|
|
| 430 |
if(name) {
|
| 431 |
try { localStorage.setItem("student_ai_user", name); } catch(e){}
|
| 432 |
currentUser = name;
|
| 433 |
+
// Hide Login -> Show Selection
|
| 434 |
+
document.getElementById("login-overlay").classList.add('hidden');
|
| 435 |
+
document.getElementById("selection-overlay").classList.remove('hidden');
|
| 436 |
+
updateEduOptions();
|
|
|
|
| 437 |
} else {
|
| 438 |
input.style.border = "1px solid red";
|
| 439 |
setTimeout(() => input.style.border = "1px solid #333", 2000);
|
| 440 |
}
|
| 441 |
}
|
| 442 |
+
|
| 443 |
+
function updateEduOptions() {
|
| 444 |
+
const level = document.getElementById('edu-level').value;
|
| 445 |
+
const yearSelect = document.getElementById('edu-year');
|
| 446 |
+
yearSelect.innerHTML = "";
|
| 447 |
+
|
| 448 |
+
let options = [];
|
| 449 |
+
if (level === 'school') {
|
| 450 |
+
options = ["6th Std", "7th Std", "8th Std", "9th Std", "10th Std", "11th Std", "12th Std"];
|
| 451 |
+
} else {
|
| 452 |
+
options = ["1st Year", "2nd Year", "3rd Year", "4th Year"];
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
options.forEach(opt => {
|
| 456 |
+
const el = document.createElement("option");
|
| 457 |
+
el.value = opt; el.innerText = opt;
|
| 458 |
+
yearSelect.appendChild(el);
|
| 459 |
+
});
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
function handleSelection() {
|
| 463 |
+
const level = document.getElementById('edu-level').value;
|
| 464 |
+
const year = document.getElementById('edu-year').value;
|
| 465 |
+
const subject = document.getElementById('edu-subject').value;
|
| 466 |
+
|
| 467 |
+
// Save Context
|
| 468 |
+
userContext = `${level}, ${year}, ${subject}`;
|
| 469 |
+
try { localStorage.setItem("student_ai_context", userContext); } catch(e){}
|
| 470 |
+
|
| 471 |
+
document.getElementById("selection-overlay").classList.add('hidden');
|
| 472 |
+
showApp();
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
function openSettings() {
|
| 476 |
+
document.getElementById("selection-overlay").classList.remove('hidden');
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
function handleLogout() {
|
| 480 |
+
try {
|
| 481 |
+
localStorage.removeItem("student_ai_user");
|
| 482 |
+
localStorage.removeItem("student_ai_context");
|
| 483 |
+
} catch(e){}
|
| 484 |
+
location.reload(); // Simple reload to reset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
}
|
| 486 |
|
| 487 |
function showApp() {
|
|
|
|
| 574 |
}
|
| 575 |
const res = await fetch('/chat', {
|
| 576 |
method: 'POST', headers: {'Content-Type': 'application/json'},
|
| 577 |
+
body: JSON.stringify({
|
| 578 |
+
message: promptText,
|
| 579 |
+
image: imgData,
|
| 580 |
+
file_text: fileText,
|
| 581 |
+
username: currentUser,
|
| 582 |
+
chat_id: currentChatId,
|
| 583 |
+
user_context: userContext // SENDING CONTEXT TO BACKEND
|
| 584 |
+
})
|
| 585 |
});
|
| 586 |
const data = await res.json();
|
| 587 |
|
|
|
|
| 736 |
box.scrollTop = box.scrollHeight;
|
| 737 |
}
|
| 738 |
|
| 739 |
+
@app.route('/manifest.json')
|
| 740 |
+
def manifest():
|
| 741 |
+
data = {
|
| 742 |
+
"name": "Student's AI",
|
| 743 |
+
"short_name": "StudentAI",
|
| 744 |
+
"start_url": "/",
|
| 745 |
+
"display": "standalone",
|
| 746 |
+
"orientation": "portrait",
|
| 747 |
+
"background_color": "#09090b",
|
| 748 |
+
"theme_color": "#09090b",
|
| 749 |
+
"icons": [
|
| 750 |
+
{
|
| 751 |
+
"src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
|
| 752 |
+
"sizes": "192x192",
|
| 753 |
+
"type": "image/png"
|
| 754 |
+
},
|
| 755 |
+
{
|
| 756 |
+
"src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
|
| 757 |
+
"sizes": "512x512",
|
| 758 |
+
"type": "image/png"
|
| 759 |
+
}
|
| 760 |
+
]
|
| 761 |
+
}
|
| 762 |
+
return Response(json.dumps(data), mimetype='application/json')
|
| 763 |
+
|
| 764 |
+
if __name__ == '__main__':
|
| 765 |
+
app.run(host='0.0.0.0', port=7860)
|
| 766 |
"""
|
| 767 |
|
| 768 |
+
# --- BACKEND ROUTES START HERE ---
|
| 769 |
@app.route("/", methods=["GET"])
|
| 770 |
def home(): return render_template_string(HTML_TEMPLATE)
|
| 771 |
|
|
|
|
| 812 |
u, cid, msg = d.get("username"), d.get("chat_id"), d.get("message")
|
| 813 |
img_data = d.get("image")
|
| 814 |
file_text = d.get("file_text")
|
| 815 |
+
user_context = d.get("user_context", "") # Capture User Context
|
| 816 |
|
| 817 |
if u not in user_db: user_db[u] = {}
|
| 818 |
if cid not in user_db[u]: user_db[u][cid] = {"messages": []}
|
| 819 |
|
| 820 |
user_db[u][cid]["messages"].append({"role": "user", "content": msg})
|
| 821 |
+
|
| 822 |
+
# PASS USER CONTEXT TO GENERATION FUNCTION
|
| 823 |
+
reply = generate_with_retry(msg, img_data, file_text, user_db[u][cid]["messages"][:-1], user_context)
|
| 824 |
+
|
| 825 |
user_db[u][cid]["messages"].append({"role": "model", "content": reply})
|
| 826 |
|
| 827 |
new_title = False
|
|
|
|
| 836 |
def manifest():
|
| 837 |
data = {
|
| 838 |
"name": "Student's AI",
|
| 839 |
+
"short_name": "StudentAI",
|
| 840 |
"start_url": "/",
|
| 841 |
"display": "standalone",
|
| 842 |
"orientation": "portrait",
|
|
|
|
| 844 |
"theme_color": "#09090b",
|
| 845 |
"icons": [
|
| 846 |
{
|
| 847 |
+
"src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
|
| 848 |
"sizes": "192x192",
|
| 849 |
"type": "image/png"
|
| 850 |
},
|
| 851 |
{
|
| 852 |
+
"src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
|
| 853 |
"sizes": "512x512",
|
| 854 |
"type": "image/png"
|
| 855 |
}
|