import os import uuid import time import json import base64 import io import warnings from PIL import Image from flask import Flask, request, jsonify, render_template_string, Response import google.generativeai as genai # --- FIX: IGNORE DEPRECATION WARNINGS --- warnings.filterwarnings("ignore") # ========================================== # 👇 API KEYS SETUP 👇 # ========================================== keys_string = os.environ.get("API_KEYS", "") API_KEYS = [k.strip() for k in keys_string.replace(',', ' ').replace('\n', ' ').split() if k.strip()] # --- 💾 DATABASE --- DB_FILE = "chat_db.json" def load_db(): try: if os.path.exists(DB_FILE): with open(DB_FILE, 'r') as f: return json.load(f) except: pass return {} def save_db(db): try: with open(DB_FILE, 'w') as f: json.dump(db, f, indent=2) except: pass user_db = load_db() current_key_index = 0 app = Flask(__name__) # --- 🧠SYSTEM INSTRUCTION --- SYSTEM_INSTRUCTION = """ ROLE: You are "Student's AI", a professional academic tutor. RULES: 1. **MATH:** Use LaTeX for formulas ($$ ... $$). 2. **DIAGRAMS:** Use Mermaid.js (```mermaid ... ```). 3. **LANGUAGE:** English by default. Use Tamil/Tanglish ONLY if requested. 4. **FORMAT:** Markdown. Bold key terms. 5. **CODE:** Use Python/Java/C++ blocks. Explain logic briefly. """ # --- 🧬 MODEL & FILE HANDLING --- def get_working_model(key): try: genai.configure(api_key=key) models = list(genai.list_models()) chat_models = [m for m in models if 'generateContent' in m.supported_generation_methods] for m in chat_models: if "flash" in m.name.lower() and "1.5" in m.name: return m.name for m in chat_models: if "pro" in m.name.lower() and "1.5" in m.name: return m.name if chat_models: return chat_models[0].name except: return None return None def process_image(image_data): try: if "base64," in image_data: image_data = image_data.split("base64,")[1] image_bytes = base64.b64decode(image_data) return Image.open(io.BytesIO(image_bytes)) except: return None def generate_with_retry(prompt, image_data=None, file_text=None, history_messages=[]): global current_key_index if not API_KEYS: return "🚨 API Keys Missing." formatted_history = [] for m in history_messages[-6:]: role = "user" if m["role"] == "user" else "model" formatted_history.append({"role": role, "parts": [m["content"]]}) current_parts = [] if file_text: current_parts.append(f"analyzing file:\n{file_text}\n\n") current_parts.append(prompt) if image_data: img = process_image(image_data) if img: current_parts.append(img) for i in range(len(API_KEYS)): key = API_KEYS[current_key_index] model_name = get_working_model(key) if not model_name: current_key_index = (current_key_index + 1) % len(API_KEYS) continue try: genai.configure(api_key=key) model = genai.GenerativeModel(model_name=model_name, system_instruction=SYSTEM_INSTRUCTION) if image_data or file_text: response = model.generate_content(current_parts) else: chat = model.start_chat(history=formatted_history) response = chat.send_message(prompt) return response.text except Exception as e: current_key_index = (current_key_index + 1) % len(API_KEYS) time.sleep(1) return "⚠️ System Busy. Please try again." # --- UI TEMPLATE (Fixed Quotes & Logic) --- HTML_TEMPLATE = """ Student's AI

Student's AI

Preview
Student's AI
""" # --- BACKEND ROUTES --- @app.route("/", methods=["GET"]) def home(): return render_template_string(HTML_TEMPLATE) @app.route("/new_chat", methods=["POST"]) def new_chat(): u = request.json.get("username") if u not in user_db: user_db[u] = {} nid = str(uuid.uuid4()) user_db[u][nid] = {"title": "New Chat", "messages": []} save_db(user_db) return jsonify({"chat_id": nid}) @app.route("/rename_chat", methods=["POST"]) def rename_chat(): d = request.json u, cid, t = d.get("username"), d.get("chat_id"), d.get("title") if u in user_db and cid in user_db[u]: user_db[u][cid]["title"] = t save_db(user_db) return jsonify({"status":"ok"}) @app.route("/delete_chat", methods=["POST"]) def delete_chat(): d = request.json u, cid = d.get("username"), d.get("chat_id") if u in user_db and cid in user_db[u]: del user_db[u][cid] save_db(user_db) return jsonify({"status":"ok"}) @app.route("/get_history", methods=["POST"]) def get_history(): u = request.json.get("username") return jsonify({"chats": user_db.get(u, {})}) @app.route("/get_chat", methods=["POST"]) def get_chat(): d = request.json return jsonify({"messages": user_db.get(d["username"], {}).get(d["chat_id"], {}).get("messages", [])}) @app.route("/chat", methods=["POST"]) def chat(): d = request.json u, cid, msg = d.get("username"), d.get("chat_id"), d.get("message") img_data = d.get("image") file_text = d.get("file_text") if u not in user_db: user_db[u] = {} if cid not in user_db[u]: user_db[u][cid] = {"messages": []} user_db[u][cid]["messages"].append({"role": "user", "content": msg}) reply = generate_with_retry(msg, img_data, file_text, user_db[u][cid]["messages"][:-1]) user_db[u][cid]["messages"].append({"role": "model", "content": reply}) new_title = False if len(user_db[u][cid]["messages"]) <= 2: user_db[u][cid]["title"] = " ".join(msg.split()[:4]) new_title = True save_db(user_db) return jsonify({"response": reply, "new_title": new_title}) @app.route('/manifest.json') def manifest(): data = { "name": "Student's AI", "short_name": "Student's AI", "start_url": "/", "display": "standalone", "orientation": "portrait", "background_color": "#09090b", "theme_color": "#09090b", "icons": [ { "src": "https://huggingface.co/spaces/Shirpi/Student-s_AI/resolve/main/1000177401.png", "sizes": "192x192", "type": "image/png" }, { "src": "https://huggingface.co/spaces/Shirpi/Student-s_AI/resolve/main/1000177401.png", "sizes": "512x512", "type": "image/png" } ] } return Response(json.dumps(data), mimetype='application/json') if __name__ == '__main__': app.run(host='0.0.0.0', port=7860)