Download app.py from OUAREDAEK/CDA-Q: direct link, hf CLI and curl.
- Browser
- Download file 4.11 kB
-
https://huggingface.co/spaces/OUAREDAEK/CDA-Q/resolve/main/app.py
- Command line
-
hf download hf://spaces/OUAREDAEK/CDA-Q/app.py
-
curl -L -o app.py https://huggingface.co/spaces/OUAREDAEK/CDA-Q/resolve/main/app.py
4.11 kB
| import os | |
| import random | |
| import torch | |
| import pandas as pd | |
| from functools import lru_cache | |
| from flask import Flask, render_template, request, jsonify | |
| from sentence_transformers import SentenceTransformer | |
| from fastapi import FastAPI | |
| from fastapi.middleware.wsgi import WSGIMiddleware | |
| import uvicorn | |
| # =============================== | |
| # CONFIG HF | |
| # =============================== | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| CSV_DATA = os.path.join(BASE_DIR, "dataset_2026.csv") | |
| EMB_FILE = os.path.join(BASE_DIR, "embeddings_questions.pt") | |
| TOP_K_RECOMMANDATIONS = 5 | |
| DEVICE = "cpu" # ⛔ FORCÉ CPU (HF) | |
| # =============================== | |
| # FLASK APP | |
| # =============================== | |
| app = Flask( | |
| __name__, | |
| template_folder=os.path.join(BASE_DIR, "templates"), | |
| static_folder=os.path.join(BASE_DIR, "static") | |
| ) | |
| # =============================== | |
| # LOAD MODEL (SAFE) | |
| # =============================== | |
| print("🔹 Loading model (CPU only)...") | |
| model = SentenceTransformer( | |
| "sentence-transformers/all-MiniLM-L6-v2", | |
| device="cpu" | |
| ) | |
| # =============================== | |
| # LOAD DATASET | |
| # =============================== | |
| print("🔹 Loading dataset...") | |
| df = pd.read_csv(CSV_DATA) | |
| df = df.dropna(subset=["question"]).reset_index(drop=True) | |
| questions = df["question"].astype(str).tolist() | |
| NB_QUESTIONS = len(questions) | |
| print(f"✅ Questions loaded: {NB_QUESTIONS}") | |
| # =============================== | |
| # LOAD / CREATE EMBEDDINGS | |
| # =============================== | |
| if os.path.exists(EMB_FILE): | |
| print("🔹 Loading cached embeddings...") | |
| emb_base = torch.load(EMB_FILE, map_location="cpu") | |
| else: | |
| print("🔹 Computing embeddings...") | |
| emb_base = model.encode( | |
| questions, | |
| convert_to_tensor=True, | |
| normalize_embeddings=True, | |
| batch_size=32 | |
| ) | |
| torch.save(emb_base, EMB_FILE) | |
| emb_base = emb_base.cpu() | |
| # =============================== | |
| # CACHE QUESTION EMBEDDING | |
| # =============================== | |
| def encode_question_cached(q: str): | |
| return model.encode( | |
| q, | |
| convert_to_tensor=True, | |
| normalize_embeddings=True | |
| ).cpu() | |
| # =============================== | |
| # UTILS | |
| # =============================== | |
| def enrich_message(base): | |
| return random.choice([ | |
| f"Bonne question 🙂 {base}", | |
| f"Voici ce que je peux vous dire : {base}", | |
| base | |
| ]) | |
| # =============================== | |
| # CORE LOGIC | |
| # =============================== | |
| def process_question(question: str): | |
| if not question.strip(): | |
| return {"response": "Veuillez poser une question."} | |
| emb_q = encode_question_cached(question).unsqueeze(0) | |
| scores = torch.matmul(emb_q, emb_base.T).squeeze(0) | |
| values, indices = torch.topk(scores, k=min(TOP_K_RECOMMANDATIONS + 1, len(scores))) | |
| best_idx = indices[0].item() | |
| confidence = int(values[0].item() * 100) | |
| if confidence < 50: | |
| return { | |
| "response": "Je ne suis pas sûr de la réponse.", | |
| "confidence": confidence | |
| } | |
| return { | |
| "response": enrich_message(df["rationale"].iloc[best_idx]), | |
| "confidence": confidence, | |
| "matched": df["question"].iloc[best_idx], | |
| "intent": df["intent"].iloc[best_idx] | |
| } | |
| # =============================== | |
| # ROUTES | |
| # =============================== | |
| def index(): | |
| return render_template("index.html") | |
| def ask(): | |
| data = request.get_json() or {} | |
| question = data.get("question", "") | |
| return jsonify(process_question(question)) | |
| # =============================== | |
| # FASTAPI WRAPPER (HF) | |
| # =============================== | |
| fastapi_app = FastAPI() | |
| fastapi_app.mount("/", WSGIMiddleware(app)) | |
| # =============================== | |
| # MAIN | |
| # =============================== | |
| if __name__ == "__main__": | |
| port = int(os.environ.get("PORT", 7860)) | |
| print(f"🚀 Running on port {port}") | |
| uvicorn.run(fastapi_app, host="0.0.0.0", port=port) | |