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:
| A | backend.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)