ada-web

Ada AI is a specialized coding...
Log | Files | Refs | README | LICENSE

commit a30ad06553878d5cc1714c893d87a504549ee551
parent 8bfacedad5bcb701945133cdf088ec61d7f9132c
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Sat, 17 Jan 2026 21:11:45 +0530

Set up Flask backend with Firebase and Gemini

Initial implementation of a Flask backend with Firebase and Gemini integration.
Diffstat:
Abackend.py | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 205 insertions(+), 0 deletions(-)

diff --git a/backend.py b/backend.py @@ -0,0 +1,205 @@ +import os +import json +import datetime +from flask import Flask, request, jsonify, Response, stream_with_context +from flask_cors import CORS +import google.generativeai as genai +import firebase_admin +from firebase_admin import credentials, auth, firestore + +# --- INIT APP & CONFIG --- +app = Flask(__name__) +# Allow CORS for your domain and localhost for testing +CORS(app, resources={r"/api/*": {"origins": ["https://amit.is-a.dev", "http://127.0.0.1:5500", "http://localhost:5000"]}}) + +# 1. Firebase Admin Init (Server-Side Security) +# Ensure you have your service account json or environment variables set up in Render +# For Render/Cloud, credentials.ApplicationDefault() often works if env vars are correct. +# Otherwise, use a service account JSON file. +try: + if not firebase_admin._apps: + # Check for service account file, otherwise assume env vars or default creds + if os.path.exists("firebase-adminsdk.json"): + cred = credentials.Certificate("firebase-adminsdk.json") + else: + cred = credentials.ApplicationDefault() + + firebase_admin.initialize_app(cred) + print("Firebase Admin Initialized") + db = firestore.client() +except Exception as e: + print(f"Firebase Init Warning: {e}") + +# 2. Gemini Init +GENAI_API_KEY = os.environ.get("GEMINI_API_KEY") +if not GENAI_API_KEY: + print("CRITICAL: GEMINI_API_KEY not found in env variables.") +else: + genai.configure(api_key=GENAI_API_KEY) + +# Generation Config +generation_config = { + "temperature": 0.2, # Low temp for precise coding + "top_p": 0.95, + "top_k": 64, + "max_output_tokens": 8192, + "response_mime_type": "text/plain", +} + +# System Instruction - THE STRICT CODING GATEKEEPER +SYSTEM_PROMPT = """ +You are 'Ada', a highly specialized Coding Assistant created by Amit Dutta. + +STRICT RULES: +1. You ONLY answer questions related to Computer Science, Programming, Code Debugging, Rewrite, or Generation. +2. If a user asks anything else (e.g., "How to cook pasta?", "Who is the president?"), politely REFUSE: "I am designed only for coding tasks." +3. FORMAT YOUR RESPONSE STRICTLY: + - Provide a helpful text explanation in Markdown format. + - If you generate, fix, or show code, place the FINAL COMPLETE CODE inside a special block: + <<<CODE_START>>> + (put the raw code here) + <<<CODE_END>>> + - Do NOT use standard markdown code fences (```) for the main code solution that belongs in the editor. You MAY use small inline code ticks `like this` in the explanation text. + - If the user provides a file or code context, USE IT. +""" + +model = genai.GenerativeModel( + model_name="gemini-1.5-flash", + generation_config=generation_config, + system_instruction=SYSTEM_PROMPT, +) + +# --- HELPER: Verify Firebase Token --- +def verify_token(auth_header): + """ + Verifies the Firebase ID Token sent from the client. + Returns user_uid if valid, None otherwise. + """ + if not auth_header or not auth_header.startswith("Bearer "): + return None + token = auth_header.split("Bearer ")[1] + try: + decoded_token = auth.verify_id_token(token) + return decoded_token['uid'] + except Exception as e: + print(f"Token verification failed: {e}") + return None + +# --- ROUTES --- + +@app.route('/') +def home(): + return "Ada AI Coding Backend is Online & Secured." + +@app.route('/api/generate-title', methods=['POST']) +def generate_title(): + """Generates a short 3-5 word title for the chat history.""" + # Verify auth even for titles to prevent abuse + user_uid = verify_token(request.headers.get('Authorization')) + if not user_uid: + return jsonify({"error": "Unauthorized"}), 401 + + data = request.json + message = data.get('message', '') + + try: + title_model = genai.GenerativeModel("gemini-1.5-flash") + res = title_model.generate_content(f"Summarize this coding query into a 3-5 word title: '{message}'") + return jsonify({"title": res.text.strip()}) + except: + return jsonify({"title": "New Chat"}) + +@app.route('/api/chat', methods=['POST']) +def chat(): + """ + Main Chat Endpoint (Streaming). + Headers: Authorization: Bearer <firebase_id_token> + Body: { "message": str, "history": list, "codeContext": str, "fileContext": str, "sessionId": str } + """ + # 1. Verify User + user_uid = verify_token(request.headers.get('Authorization')) + if not user_uid: + return jsonify({"error": "Unauthorized"}), 401 + + data = request.json + user_msg = data.get('message') + history = data.get('history', []) + code_ctx = data.get('codeContext', '') + file_ctx = data.get('fileContext', '') + session_id = data.get('sessionId') + + if not user_msg: + return jsonify({"error": "Empty message"}), 400 + + # 2. Construct Prompt with Context + context_str = "" + if code_ctx: + context_str += f"\n\n[CURRENT EDITOR CONTENT]:\n{code_ctx}\n" + if file_ctx: + context_str += f"\n\n[UPLOADED FILE CONTENT]:\n{file_ctx}\n" + + # Add History (Gemini format) + chat_history = [] + for turn in history: + # Gemini expects 'user' and 'model' roles + chat_history.append({"role": "user", "parts": [turn.get('user', '')]}) + chat_history.append({"role": "model", "parts": [turn.get('model', '')]}) + + chat_session = model.start_chat(history=chat_history) + + # 3. Stream Response + def generate(): + final_text_acc = "" + + prompt_with_ctx = user_msg + context_str + + try: + response = chat_session.send_message(prompt_with_ctx, stream=True) + + for chunk in response: + if chunk.text: + yield chunk.text + final_text_acc += chunk.text + + # 4. Save Interaction to Firestore + # We save this asynchronously (conceptually) after streaming is done. + if session_id: + try: + doc_ref = db.collection('users').document(user_uid).collection('chats').document(session_id) + + msg_data = { + "role": "user", + "content": user_msg, + "timestamp": datetime.datetime.now(datetime.timezone.utc) + } + + # We store the raw AI response. The client parses the code blocks. + ai_data = { + "role": "model", + "content": final_text_acc, + "timestamp": datetime.datetime.now(datetime.timezone.utc) + } + + doc = doc_ref.get() + if not doc.exists: + doc_ref.set({ + "createdAt": datetime.datetime.now(datetime.timezone.utc), + "userId": user_uid, + "messages": [msg_data, ai_data] + }) + else: + doc_ref.update({ + "messages": firestore.ArrayUnion([msg_data, ai_data]), + "updatedAt": datetime.datetime.now(datetime.timezone.utc) + }) + except Exception as db_err: + print(f"Database Save Error: {db_err}") + + except Exception as e: + yield f"Error: {str(e)}" + + return Response(stream_with_context(generate()), mimetype='text/plain') + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 5000)) + app.run(host='0.0.0.0', port=port, debug=True)
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror