CoolFace
Apppublic

seifataa/Flashcards

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
app.py131 linesDownload Raw Back to root
1import os2import time3import json4from pathlib import Path5from flask import Flask, request, jsonify6from docx import Document7from pdfminer.high_level import extract_text as extract_pdf8import google.generativeai as genai9 10# ------------------------------11# Configure Gemini API keys12# ------------------------------13# Space secrets: GOOGLE_API_KEY_1 to GOOGLE_API_KEY_614API_KEYS = [15    os.getenv(f"GOOGLE_API_KEY_{i}") for i in range(1, 9)16]17if not any(API_KEYS):18    raise ValueError("Please set at least one GOOGLE_API_KEY_(1-6) secret in the Space settings.")19 20# Round-robin key tracker21current_key_index = 022MODEL_NAME = "gemini-flash-lite-latest"  # lighter model23 24def switch_key():25    global current_key_index26    current_key_index = (current_key_index + 1) % len(API_KEYS)27    genai.configure(api_key=API_KEYS[current_key_index])28    return API_KEYS[current_key_index]29 30# Initialize first key31genai.configure(api_key=API_KEYS[current_key_index])32gemini = genai.GenerativeModel(MODEL_NAME)33 34# ------------------------------35# Flask app setup36# ------------------------------37app = Flask(__name__)38 39# ------------------------------40# Helper functions41# ------------------------------42def extract_text(file_path):43    ext = os.path.splitext(file_path)[1].lower()44    if ext == ".pdf":45        return extract_pdf(file_path)46    elif ext == ".docx":47        doc = Document(file_path)48        return "\n".join([p.text for p in doc.paragraphs])49    else:50        try:51            with open(file_path, "r", encoding="utf-8") as f:52                return f.read()53        except Exception:54            return ""55 56def chunk_text(text, max_len=2500):57    chunks = []58    start = 059    while start < len(text):60        chunks.append(text[start:start+max_len])61        start += max_len62    return chunks63 64def safe_generate(prompt, retries=6):65    """Call Gemini API safely with multi-key rotation and retry on quota errors."""66    attempt = 067    while attempt < retries:68        try:69            resp = gemini.generate_content(prompt)70            if resp.text:71                return resp.text.strip()72            return ""73        except Exception as e:74            err = str(e).lower()75            if "quota" in err or "429" in err:76                print(f"⚠️ Quota exceeded on key #{current_key_index + 1}, switching key...")77                switch_key()78                time.sleep(5)  # small delay before retry79                attempt += 180            else:81                raise82    return "⚠️ Could not generate flashcards due to API limits."83 84# ------------------------------85# Flashcard generation endpoint86# ------------------------------87@app.route("/generate", methods=["POST"])88def generate_flashcards():89    if "file" not in request.files:90        return jsonify({"error": "No file uploaded"}), 40091 92    file = request.files["file"]93    temp_path = f"/tmp/{file.filename}"94    file.save(temp_path)95 96    text = extract_text(temp_path)97    if not text or len(text.strip()) < 50:98        return jsonify({"error": "File too short or empty"}), 40099 100    chunks = chunk_text(text)101    all_flashcards = []102 103    for idx, chunk in enumerate(chunks, start=1):104        prompt = (105            "Generate 3 concise medical flashcards from this text. "106            "Respond ONLY with a JSON array. Format strictly as: "107            '[{"front": "Question", "back": "Answer"}], separated by a semi-colon. '108            "Do NOT add extra commentary or explanations.\n\n"109            f"Text:\n{chunk}"110        )111 112        print(f"🧩 Processing chunk {idx}/{len(chunks)}...")113        result = safe_generate(prompt)114 115        try:116            cards = json.loads(result)117            if isinstance(cards, list):118                all_flashcards.extend(cards)119            else:120                all_flashcards.append({"front": "Parsing Error", "back": result})121        except Exception:122            all_flashcards.append({"front": "Parsing Error", "back": result})123 124    return jsonify({"data": all_flashcards})125 126@app.route("/", methods=["GET"])127def root():128    return jsonify({"status": "running", "message": "Flashcard backend ready!"})129 130if __name__ == "__main__":131    app.run(host="0.0.0.0", port=7860)