Shirpi commited on
Commit
03c12d0
·
verified ·
1 Parent(s): 3905f1d

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -317
app.py DELETED
@@ -1,317 +0,0 @@
1
- import os
2
- import uuid
3
- import time
4
- import json
5
- import markdown
6
- from flask import Flask, request, jsonify, render_template_string
7
- from google import genai
8
- from google.genai import types
9
-
10
- # ==========================================
11
- # 👇 API KEYS SETUP (DEBUG MODE) 👇
12
- # ==========================================
13
- keys_string = os.environ.get("API_KEYS", "")
14
- # Split by comma or newline or space to be safe
15
- API_KEYS = [k.strip() for k in keys_string.replace(',', ' ').replace('\n', ' ').split() if k.strip()]
16
-
17
- # LOGGING TO CONSOLE (Check Hugging Face Logs)
18
- print(f"✅ Loaded {len(API_KEYS)} API Keys.")
19
-
20
- if not API_KEYS:
21
- print("⚠️ Warning: API Keys set aagala! Secrets la 'API_KEYS' check pannunga.")
22
-
23
- current_key_index = 0
24
- app = Flask(__name__)
25
-
26
- # --- 💾 DATABASE ---
27
- DB_FILE = "chat_db.json"
28
-
29
- def load_db():
30
- try:
31
- if os.path.exists(DB_FILE):
32
- with open(DB_FILE, 'r') as f: return json.load(f)
33
- except: pass
34
- return {}
35
-
36
- def save_db(db):
37
- try:
38
- with open(DB_FILE, 'w') as f: json.dump(db, f, indent=2)
39
- except Exception as e:
40
- print(f"Save Error: {e}")
41
-
42
- user_db = load_db()
43
-
44
- # --- 🧠 SYSTEM INSTRUCTION ---
45
- SYSTEM_INSTRUCTION = """
46
- ROLE: You are "Student's AI", a coding tutor.
47
- PERMANENT RULES:
48
- 1. **BLACK BOX:** Always format code inside Markdown blocks (```python ... ```).
49
- 2. **INDENTATION:** Code must have perfect spacing.
50
- 3. **LANGUAGE:** Tamil & English mix (Tanglish).
51
- 4. **BRIEF:** Keep explanations short and crisp.
52
- """
53
-
54
- def generate_with_retry(prompt, history_messages=[]):
55
- global current_key_index
56
-
57
- # Check if keys exist first
58
- if not API_KEYS:
59
- return "❌ Error: API Keys List is Empty. Check Hugging Face Secrets."
60
-
61
- recent_history = history_messages[-10:]
62
- formatted_history = []
63
- for msg in recent_history:
64
- role = "user" if msg["role"] == "user" else "model"
65
- formatted_history.append(types.Content(role=role, parts=[types.Part.from_text(text=msg["content"])]))
66
-
67
- formatted_history.append(types.Content(role="user", parts=[types.Part.from_text(text=prompt)]))
68
-
69
- last_error = ""
70
-
71
- # RETRY LOOP
72
- for attempt in range(len(API_KEYS) * 2): # Try twice per key
73
- key = API_KEYS[current_key_index]
74
-
75
- try:
76
- client = genai.Client(api_key=key)
77
- response = client.models.generate_content(
78
- model="gemini-1.5-flash",
79
- contents=formatted_history,
80
- config=types.GenerateContentConfig(system_instruction=SYSTEM_INSTRUCTION, temperature=0.7, max_output_tokens=1500)
81
- )
82
- return response.text
83
-
84
- except Exception as e:
85
- error_msg = str(e)
86
- print(f"⚠️ Key #{current_key_index+1} Failed: {error_msg}")
87
- last_error = error_msg
88
-
89
- # Switch Key
90
- current_key_index = (current_key_index + 1) % len(API_KEYS)
91
- time.sleep(1) # Wait 1 second
92
- continue
93
-
94
- # Return the REAL error to the user UI
95
- return f"⚠️ **Google API Error:** {last_error}<br>Please check your API Keys."
96
-
97
- # --- UI TEMPLATE ---
98
- HTML_TEMPLATE = """
99
- <!DOCTYPE html>
100
- <html lang="en">
101
- <head>
102
- <meta charset="UTF-8">
103
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
104
- <title>Student's AI</title>
105
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
106
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
107
- <style>
108
- :root { --bg: #000; --sidebar: #0a0a0a; --header: rgba(0,0,0,0.9); --user-bg: #222; --text: #ececec; }
109
- * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
110
- body, html { margin: 0; padding: 0; height: 100%; width: 100%; background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; overflow: hidden; }
111
- pre { background: #161616; border: 1px solid #333; border-radius: 8px; padding: 15px; overflow-x: auto; font-family: 'JetBrains Mono', monospace; }
112
- code { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: #e6edf3; }
113
- .k { color: #ff7b72; } .s { color: #a5d6ff; } .c { color: #8b949e; } .nf { color: #d2a8ff; } .m { color: #79c0ff; }
114
- #app-container { display: none; flex-direction: column; height: 100%; }
115
- header { height: 60px; padding: 0 20px; background: var(--header); border-bottom: 1px solid #222; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; z-index: 50; }
116
- #sidebar { position: fixed; inset: 0; width: 280px; background: var(--sidebar); border-right: 1px solid #222; transform: translateX(-100%); transition: 0.3s; z-index: 100; padding: 20px; display: flex; flex-direction: column; }
117
- #sidebar.open { transform: translateX(0); }
118
- .overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 90; display: none; }
119
- .overlay.active { display: block; }
120
- #chat-box { flex: 1; overflow-y: auto; padding: 20px 20px 40px 20px; display: flex; flex-direction: column; gap: 20px; }
121
- .msg { display: flex; flex-direction: column; width: 100%; animation: fade 0.3s forwards; }
122
- @keyframes fade { from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:translateY(0);} }
123
- .user-msg { align-items: flex-end; }
124
- .user-msg .bubble { background: var(--user-bg); padding: 12px 18px; border-radius: 18px 18px 4px 18px; max-width: 85%; }
125
- .ai-msg { align-items: flex-start; }
126
- .ai-text { width: 100%; line-height: 1.6; font-size: 16px; color: #eee; }
127
- .ai-text p:first-child { margin-top: 0; }
128
- .input-wrap { padding: 15px 20px; background: #000; border-top: 1px solid #222; flex-shrink: 0; z-index: 60; }
129
- .input-bar { width: 100%; max-width: 800px; margin: 0 auto; background: #161616; border: 1px solid #333; border-radius: 30px; padding: 8px 12px 8px 20px; display: flex; align-items: center; gap: 10px; }
130
- textarea { flex: 1; background: transparent; border: none; color: #fff; font-size: 16px; max-height: 100px; padding: 5px 0; resize: none; outline: none; font-family: 'Inter', sans-serif; }
131
- .send-btn { width: 40px; height: 40px; min-width: 40px; background: #fff; color: #000; border-radius: 50%; border: none; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: 0.2s; z-index: 10; }
132
- .send-btn:active { transform: scale(0.9); background: #ddd; }
133
- #login-overlay { position: fixed; inset: 0; z-index: 2000; background: #000; display: flex; flex-direction: column; align-items: center; justify-content: center; }
134
- .login-box { width: 90%; max-width: 400px; padding: 40px; background: #111; border: 1px solid #333; border-radius: 20px; text-align: center; }
135
- .login-input { width: 100%; padding: 15px; background: #000; border: 1px solid #333; color: #fff; border-radius: 10px; margin-bottom: 20px; text-align: center; }
136
- .login-btn { width: 100%; padding: 15px; background: #fff; color: #000; border: none; border-radius: 10px; font-weight: 700; cursor: pointer; }
137
- </style>
138
- </head>
139
- <body>
140
- <div id="login-overlay">
141
- <div class="login-box">
142
- <h2 style="color:#fff;">Student's AI</h2>
143
- <input type="text" id="username-input" class="login-input" placeholder="Enter Name" autocomplete="off">
144
- <button onclick="handleLogin()" class="login-btn">Start</button>
145
- </div>
146
- </div>
147
- <div id="app-container">
148
- <div class="overlay" id="overlay" onclick="toggleSidebar()"></div>
149
- <div id="sidebar">
150
- <div style="padding-bottom:20px; border-bottom:1px solid #222; margin-bottom:20px; font-weight:bold; display:flex; gap:10px; align-items:center;">
151
- <div id="avatar" style="width:35px; height:35px; background:#333; border-radius:50%; display:flex; align-items:center; justify-content:center;">U</div>
152
- <span id="display-name">User</span>
153
- </div>
154
- <div style="background:#fff; color:#000; padding:12px; border-radius:8px; text-align:center; font-weight:bold; cursor:pointer;" onclick="newChat()">+ New Chat</div>
155
- <div style="flex:1; overflow-y:auto; margin-top:20px;" id="history-list"></div>
156
- <div style="margin-top:20px; color:#ff5555; cursor:pointer; text-align:center;" onclick="handleLogout()">Log Out</div>
157
- </div>
158
- <header>
159
- <i class="fas fa-bars" style="color:#fff; font-size:20px; cursor:pointer;" onclick="toggleSidebar()"></i>
160
- <div style="font-weight:bold; font-size:18px; color:#fff;">Student's AI</div>
161
- <div style="width:20px;"></div>
162
- </header>
163
- <div id="chat-box"></div>
164
- <div class="input-wrap">
165
- <div class="input-bar">
166
- <textarea id="input" placeholder="Type here..." rows="1" oninput="resizeInput(this)"></textarea>
167
- <button id="sendBtn" class="send-btn" onclick="send(event)"><i class="fas fa-arrow-up"></i></button>
168
- </div>
169
- </div>
170
- </div>
171
- <script>
172
- let currentUser = null; let currentChatId = null;
173
- function resizeInput(el) { el.style.height = 'auto'; el.style.height = Math.min(el.scrollHeight, 100) + 'px'; }
174
- document.getElementById("input").addEventListener("keydown", function(e) { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(e); }});
175
- function checkLogin() {
176
- const stored = localStorage.getItem("student_ai_user");
177
- if (stored) { currentUser = stored; showApp(); } else { document.getElementById("login-overlay").style.display = "flex"; }
178
- }
179
- function handleLogin() {
180
- const name = document.getElementById("username-input").value.trim();
181
- if(name) { localStorage.setItem("student_ai_user", name); currentUser = name; showApp(); }
182
- }
183
- function handleLogout() { localStorage.removeItem("student_ai_user"); location.reload(); }
184
- function showApp() {
185
- document.getElementById("login-overlay").style.display = "none";
186
- document.getElementById("app-container").style.display = "flex";
187
- document.getElementById("display-name").innerText = currentUser;
188
- document.getElementById("avatar").innerText = currentUser[0].toUpperCase();
189
- loadHistory();
190
- if(!currentChatId) {
191
- const box = document.getElementById("chat-box");
192
- if(box.innerHTML.trim() === "") { box.innerHTML = `<div class="msg ai-msg"><div class="ai-text"><strong>Welcome ${currentUser}!</strong><br>I am ready.</div></div>`; }
193
- }
194
- }
195
- const chatBox = document.getElementById('chat-box');
196
- const input = document.getElementById('input');
197
- async function send(event) {
198
- if(event) event.preventDefault();
199
- const text = input.value.trim();
200
- if(!text) return;
201
- input.value = ''; input.style.height = 'auto'; input.focus();
202
- const userDiv = document.createElement('div'); userDiv.className = 'msg user-msg';
203
- userDiv.innerHTML = `<div class="bubble">${text}</div>`;
204
- chatBox.appendChild(userDiv); chatBox.scrollTop = chatBox.scrollHeight;
205
- const tempId = "ai-" + Date.now();
206
- const aiDiv = document.createElement('div'); aiDiv.className = 'msg ai-msg'; aiDiv.id = tempId;
207
- aiDiv.innerHTML = `<div class="ai-text"><i class="fas fa-circle-notch fa-spin"></i> Thinking...</div>`;
208
- chatBox.appendChild(aiDiv); chatBox.scrollTop = chatBox.scrollHeight;
209
- try {
210
- if(!currentChatId) {
211
- const res = await fetch('/new_chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username: currentUser }) });
212
- if(!res.ok) throw new Error("Net Error");
213
- const data = await res.json();
214
- currentChatId = data.chat_id;
215
- }
216
- const res = await fetch('/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ message: text, username: currentUser, chat_id: currentChatId }) });
217
- if(!res.ok) throw new Error("API Error");
218
- const data = await res.json();
219
- document.getElementById(tempId).remove();
220
- const finalAiDiv = document.createElement('div'); finalAiDiv.className = 'msg ai-msg';
221
- finalAiDiv.innerHTML = `<div class="ai-text">${data.response || "Error"}</div>`;
222
- chatBox.appendChild(finalAiDiv);
223
- if(data.new_title) loadHistory();
224
- } catch(e) { document.getElementById(tempId).innerHTML = `<div class="ai-text" style="color:#ff5555">⚠️ Failed: ${e.message}</div>`; }
225
- chatBox.scrollTop = chatBox.scrollHeight;
226
- }
227
- async function loadHistory() {
228
- try {
229
- const res = await fetch('/get_history', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username: currentUser }) });
230
- const data = await res.json();
231
- const list = document.getElementById("history-list"); list.innerHTML = "";
232
- if(data.chats) Object.keys(data.chats).reverse().forEach(cid => { list.innerHTML += `<div style="padding:10px; color:#888; cursor:pointer;" onclick="loadChat('${cid}')"><i class="far fa-comment"></i> ${data.chats[cid].title}</div>`; });
233
- } catch(e) {}
234
- }
235
- async function newChat(silent=false) {
236
- try {
237
- const res = await fetch('/new_chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username: currentUser }) });
238
- const data = await res.json(); currentChatId = data.chat_id;
239
- if(!silent) { chatBox.innerHTML = ''; loadHistory(); toggleSidebar(); }
240
- } catch(e) {}
241
- }
242
- async function loadChat(cid) {
243
- currentChatId = cid;
244
- const res = await fetch('/get_chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username: currentUser, chat_id: cid }) });
245
- const data = await res.json(); chatBox.innerHTML = '';
246
- data.messages.forEach(msg => {
247
- const div = document.createElement('div'); div.className = msg.role === 'user' ? 'msg user-msg' : 'msg ai-msg';
248
- div.innerHTML = msg.role === 'user' ? `<div class="bubble">${msg.content}</div>` : `<div class="ai-text">${msg.content}</div>`;
249
- chatBox.appendChild(div);
250
- });
251
- toggleSidebar(); chatBox.scrollTop = chatBox.scrollHeight;
252
- }
253
- function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); document.getElementById('overlay').classList.toggle('active'); }
254
- checkLogin();
255
- </script>
256
- </body>
257
- </html>
258
- """
259
-
260
- # --- BACKEND ROUTES ---
261
- @app.route("/", methods=["GET"])
262
- def home(): return render_template_string(HTML_TEMPLATE)
263
-
264
- @app.route("/get_history", methods=["POST"])
265
- def get_history():
266
- user = request.json.get("username")
267
- return jsonify({"chats": user_db.get(user, {})})
268
-
269
- @app.route("/new_chat", methods=["POST"])
270
- def new_chat():
271
- user = request.json.get("username")
272
- if user not in user_db: user_db[user] = {}
273
- nid = str(uuid.uuid4())
274
- user_db[user][nid] = {"title": "New Chat", "messages": []}
275
- save_db(user_db)
276
- return jsonify({"status": "success", "chat_id": nid})
277
-
278
- @app.route("/get_chat", methods=["POST"])
279
- def get_chat():
280
- data = request.json
281
- return jsonify({"messages": user_db.get(data.get("username"), {}).get(data.get("chat_id"), {}).get("messages", [])})
282
-
283
- @app.route("/chat", methods=["POST"])
284
- def chat():
285
- data = request.json
286
- msg = data.get("message")
287
- user = data.get("username")
288
- cid = data.get("chat_id")
289
-
290
- if user not in user_db: user_db[user] = {}
291
- if cid not in user_db[user]:
292
- user_db[user][cid] = {"title": "New Chat", "messages": []}
293
- save_db(user_db)
294
-
295
- user_db[user][cid]["messages"].append({"role": "user", "content": msg})
296
- new_title = False
297
- if len(user_db[user][cid]["messages"]) == 1:
298
- user_db[user][cid]["title"] = " ".join(msg.split()[:4]) + "..."
299
- new_title = True
300
-
301
- save_db(user_db)
302
-
303
- try:
304
- previous_messages = user_db[user][cid]['messages'][:-1]
305
- raw_response = generate_with_retry(msg, previous_messages)
306
- formatted_html = markdown.markdown(raw_response, extensions=['fenced_code', 'codehilite'])
307
-
308
- user_db[user][cid]["messages"].append({"role": "ai", "content": formatted_html})
309
- save_db(user_db)
310
-
311
- return jsonify({"response": formatted_html, "new_title": new_title})
312
-
313
- except Exception as e:
314
- return jsonify({"error": str(e)})
315
-
316
- if __name__ == '__main__':
317
- app.run(host='0.0.0.0', port=7860)