Download app.py from devansh1a/AISPY: direct link, hf CLI and curl.
- Browser
- Download file 16.7 kB
-
https://huggingface.co/spaces/devansh1a/AISPY/resolve/main/app.py
- Command line
-
hf download hf://spaces/devansh1a/AISPY/app.py
-
curl -L -o app.py https://huggingface.co/spaces/devansh1a/AISPY/resolve/main/app.py
16.7 kB
| import functools | |
| import json | |
| import os | |
| import uuid | |
| from typing import Any, Dict | |
| from dotenv import load_dotenv | |
| from flask import Flask, abort, flash, jsonify, redirect, render_template, request, session, url_for | |
| from flask import Response | |
| from werkzeug.utils import secure_filename | |
| from core.observability import init_langsmith_observability | |
| from core.storage import ( | |
| create_password_reset_token, | |
| change_password, | |
| create_analysis, | |
| create_user, | |
| get_admin_stats, | |
| get_analysis_by_id, | |
| get_dashboard_stats, | |
| get_user_by_id, | |
| get_user_by_email, | |
| init_db, | |
| list_all_analyses, | |
| list_users, | |
| latest_analysis_for_user, | |
| list_user_analyses, | |
| seed_first_admin, | |
| set_user_admin, | |
| get_password_reset_token, | |
| use_password_reset_token, | |
| update_user_profile, | |
| verify_user, | |
| ) | |
| from core.workflow import run_aispy_pipeline | |
| from utils import downloader | |
| load_dotenv() | |
| app = Flask(__name__) | |
| app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", os.getenv("FLASK_SECRET_KEY", "aispy-dev-secret")) | |
| app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024 | |
| app.config["SESSION_COOKIE_HTTPONLY"] = True | |
| app.config["SESSION_COOKIE_SAMESITE"] = "Lax" | |
| ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp", "mp4", "mov", "avi"} | |
| def allowed_file(filename: str) -> bool: | |
| return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS | |
| def to_jsonable(value: Any): | |
| if value is None: | |
| return None | |
| if isinstance(value, (str, int, float, bool)): | |
| return value | |
| if isinstance(value, dict): | |
| return {str(k): to_jsonable(v) for k, v in value.items()} | |
| if isinstance(value, (list, tuple, set)): | |
| return [to_jsonable(v) for v in value] | |
| if hasattr(value, "model_dump"): | |
| return to_jsonable(value.model_dump()) | |
| if hasattr(value, "dict"): | |
| try: | |
| return to_jsonable(value.dict()) | |
| except Exception: | |
| pass | |
| if hasattr(value, "__dict__"): | |
| return to_jsonable({k: v for k, v in value.__dict__.items() if not k.startswith("_")}) | |
| return str(value) | |
| def extract_value(obj: Any, key: str, default=None): | |
| if obj is None: | |
| return default | |
| if hasattr(obj, key): | |
| return getattr(obj, key) | |
| if isinstance(obj, dict): | |
| return obj.get(key, default) | |
| return default | |
| def current_user(): | |
| user_id = session.get("user_id") | |
| if not user_id: | |
| return None | |
| return { | |
| "id": user_id, | |
| "name": session.get("user_name"), | |
| "email": session.get("user_email"), | |
| "is_admin": bool(session.get("user_is_admin")), | |
| } | |
| def login_required(view): | |
| def wrapped(*args, **kwargs): | |
| if not session.get("user_id"): | |
| if request.path == "/analyze" or request.path.startswith("/api/"): | |
| return jsonify({"error": "Authentication required."}), 401 | |
| flash("Please sign in to continue.", "warning") | |
| return redirect(url_for("login")) | |
| return view(*args, **kwargs) | |
| return wrapped | |
| def admin_required(view): | |
| def wrapped(*args, **kwargs): | |
| if not session.get("user_id"): | |
| if request.path == "/analyze" or request.path.startswith("/api/"): | |
| return jsonify({"error": "Authentication required."}), 401 | |
| flash("Please sign in to continue.", "warning") | |
| return redirect(url_for("login")) | |
| if not session.get("user_is_admin"): | |
| abort(403) | |
| return view(*args, **kwargs) | |
| return wrapped | |
| def inject_globals(): | |
| return {"current_user": current_user()} | |
| def initialize(): | |
| init_db() | |
| seed_first_admin() | |
| init_langsmith_observability() | |
| def index(): | |
| if session.get("user_id"): | |
| return redirect(url_for("dashboard")) | |
| return redirect(url_for("login")) | |
| def register(): | |
| if session.get("user_id"): | |
| return redirect(url_for("dashboard")) | |
| if request.method == "POST": | |
| name = request.form.get("name", "").strip() | |
| email = request.form.get("email", "").strip().lower() | |
| password = request.form.get("password", "") | |
| confirm = request.form.get("confirm_password", "") | |
| if not name or not email or not password: | |
| flash("All fields are required.", "danger") | |
| elif password != confirm: | |
| flash("Passwords do not match.", "danger") | |
| elif get_user_by_email(email): | |
| flash("An account already exists for that email.", "danger") | |
| else: | |
| user = create_user(name, email, password) | |
| session["user_id"] = user["id"] | |
| session["user_name"] = user["name"] | |
| session["user_email"] = user["email"] | |
| session["user_is_admin"] = bool(user.get("is_admin")) | |
| flash("Account created successfully.", "success") | |
| return redirect(url_for("dashboard")) | |
| return render_template("register.html") | |
| def login(): | |
| if session.get("user_id"): | |
| return redirect(url_for("dashboard")) | |
| if request.method == "POST": | |
| email = request.form.get("email", "").strip().lower() | |
| password = request.form.get("password", "") | |
| user = verify_user(email, password) | |
| if not user: | |
| flash("Invalid email or password.", "danger") | |
| else: | |
| session["user_id"] = user["id"] | |
| session["user_name"] = user["name"] | |
| session["user_email"] = user["email"] | |
| session["user_is_admin"] = bool(user.get("is_admin")) | |
| flash("Welcome back.", "success") | |
| return redirect(url_for("dashboard")) | |
| return render_template("login.html") | |
| def logout(): | |
| session.clear() | |
| flash("You have been signed out.", "info") | |
| return redirect(url_for("login")) | |
| def forgot_password(): | |
| if request.method == "POST": | |
| email = request.form.get("email", "").strip().lower() | |
| token_info = create_password_reset_token(email) | |
| if token_info: | |
| reset_link = url_for("reset_password", token=token_info["token"], _external=True) | |
| flash(f"Reset link created: {reset_link}", "success") | |
| return render_template("forgot_password.html", reset_link=reset_link, email=email) | |
| flash("If the account exists, a reset link can be generated.", "info") | |
| return render_template("forgot_password.html") | |
| def reset_password(token: str): | |
| record = get_password_reset_token(token) | |
| if not record: | |
| flash("Invalid or expired reset token.", "danger") | |
| return redirect(url_for("forgot_password")) | |
| if request.method == "POST": | |
| new_password = request.form.get("new_password", "") | |
| confirm_password = request.form.get("confirm_password", "") | |
| if not new_password: | |
| flash("Please enter a new password.", "danger") | |
| elif new_password != confirm_password: | |
| flash("Passwords do not match.", "danger") | |
| else: | |
| used = use_password_reset_token(token) | |
| if not used: | |
| flash("This reset link has already been used or expired.", "danger") | |
| return redirect(url_for("forgot_password")) | |
| change_password(record["user_id"], new_password) | |
| flash("Password updated. Please sign in.", "success") | |
| return redirect(url_for("login")) | |
| return render_template("reset_password.html", token=token, email=record.get("email")) | |
| def dashboard(): | |
| user = get_user_by_email(session.get("user_email", "")) | |
| analyses = list_user_analyses(session["user_id"], limit=8) | |
| stats = get_dashboard_stats(session["user_id"]) | |
| return render_template( | |
| "dashboard.html", | |
| user=user, | |
| stats=stats, | |
| analyses=analyses, | |
| ) | |
| def settings(): | |
| user = get_user_by_email(session.get("user_email", "")) | |
| if request.method == "POST": | |
| name = request.form.get("name", "").strip() | |
| email = request.form.get("email", "").strip().lower() | |
| current_password = request.form.get("current_password", "") | |
| new_password = request.form.get("new_password", "") | |
| if not user: | |
| abort(404) | |
| if name and email: | |
| try: | |
| updated = update_user_profile(user["id"], name=name, email=email) | |
| session["user_name"] = updated["name"] | |
| session["user_email"] = updated["email"] | |
| session["user_is_admin"] = bool(updated.get("is_admin")) | |
| flash("Profile updated.", "success") | |
| except Exception: | |
| flash("Email is already in use.", "danger") | |
| if new_password: | |
| if not verify_user(user["email"], current_password): | |
| flash("Current password is incorrect.", "danger") | |
| else: | |
| change_password(user["id"], new_password) | |
| flash("Password updated.", "success") | |
| return redirect(url_for("settings")) | |
| return render_template("settings.html", user=user) | |
| def admin_dashboard(): | |
| return render_template( | |
| "admin.html", | |
| admin_stats=get_admin_stats(), | |
| users=list_users(limit=200), | |
| analyses=list_all_analyses(limit=100), | |
| ) | |
| def admin_toggle(user_id: int): | |
| current = get_user_by_email(session.get("user_email", "")) | |
| target = get_user_by_id(user_id) | |
| if not target: | |
| abort(404) | |
| if current and current.get("id") == target.get("id") and target.get("is_admin"): | |
| flash("Cannot remove your own admin role.", "warning") | |
| return redirect(url_for("admin_dashboard")) | |
| if target.get("is_admin") and get_admin_stats().get("admins", 0) <= 1: | |
| flash("At least one admin account must remain active.", "warning") | |
| return redirect(url_for("admin_dashboard")) | |
| set_user_admin(user_id, not bool(target.get("is_admin"))) | |
| flash("Admin role updated.", "success") | |
| return redirect(url_for("admin_dashboard")) | |
| def analyze(): | |
| file_obj = request.files.get("file") | |
| user_input_url = request.form.get("input", "").strip() | |
| text_claim = request.form.get("text_claim", "").strip() | |
| if not file_obj and not user_input_url and not text_claim: | |
| return jsonify({"error": "Please provide a media file, URL, or text claim."}), 400 | |
| media_path = None | |
| input_type = "text" | |
| request_id = str(uuid.uuid4()) | |
| file_name = None | |
| try: | |
| if file_obj and file_obj.filename: | |
| if not allowed_file(file_obj.filename): | |
| return jsonify({"error": "Invalid file type."}), 400 | |
| file_name = secure_filename(file_obj.filename) | |
| file_prefix = f"{request_id[:8]}_{file_name}" | |
| media_path = os.path.join(downloader.DOWNLOAD_FOLDER, file_prefix) | |
| file_obj.save(media_path) | |
| input_type = "media" | |
| elif user_input_url and user_input_url.startswith("http"): | |
| media_path, _ = downloader.download_media(user_input_url) | |
| if not media_path: | |
| return jsonify({"error": "Could not download content from link."}), 400 | |
| file_name = os.path.basename(media_path) | |
| input_type = "media" | |
| final_state = run_aispy_pipeline( | |
| input_type=input_type, | |
| media_path=media_path, | |
| text_claim=text_claim if text_claim else None, | |
| request_id=request_id, | |
| ) | |
| if final_state.get("errors") and not final_state.get("final_result"): | |
| return jsonify({"error": "Analysis failed: " + " | ".join(final_state["errors"])}), 500 | |
| verdict_data = final_state.get("final_result") | |
| forensics_data = final_state.get("forensics_data") | |
| osint_data = final_state.get("osint_data") | |
| if not verdict_data: | |
| return jsonify({"error": "The AI auditor failed to generate a verdict."}), 500 | |
| response_data = { | |
| "verdict": extract_value(verdict_data, "verdict", "No verdict"), | |
| "confidence": extract_value(verdict_data, "confidence", 0.0), | |
| "reasoning": f"{extract_value(verdict_data, 'reasoning', '')} {extract_value(verdict_data, 'xai_breakdown', '')}".strip(), | |
| "final_report": final_state.get("final_report", ""), | |
| "extracted_context": final_state.get("media_context", "") or extract_value(forensics_data, "extracted_caption", "No vision context."), | |
| "visual_evidence": extract_value(forensics_data, "visual_evidence", "No visual forensics run."), | |
| "context_sources": [{"url": url, "title": "Verified Source"} for url in (extract_value(osint_data, "sources_used", []) or [])], | |
| "request_id": request_id, | |
| } | |
| analysis = create_analysis( | |
| { | |
| "user_id": session["user_id"], | |
| "request_id": request_id, | |
| "input_type": input_type, | |
| "file_name": file_name, | |
| "source_url": user_input_url or None, | |
| "text_claim": text_claim or None, | |
| "verdict": response_data["verdict"], | |
| "confidence": response_data["confidence"], | |
| "reasoning": response_data["reasoning"], | |
| "final_report": response_data["final_report"], | |
| "payload_json": { | |
| "response": to_jsonable(response_data), | |
| "final_state": to_jsonable(final_state), | |
| }, | |
| } | |
| ) | |
| return jsonify( | |
| { | |
| **response_data, | |
| "analysis_id": analysis["id"], | |
| "report_url": url_for("report_view", analysis_id=analysis["id"]), | |
| } | |
| ) | |
| except Exception as exc: | |
| return jsonify({"error": str(exc)}), 500 | |
| finally: | |
| if media_path: | |
| downloader.cleanup_file(media_path) | |
| def reports(): | |
| analyses = list_user_analyses(session["user_id"], limit=50) | |
| return render_template("reports.html", analyses=analyses) | |
| def report_latest(): | |
| latest = latest_analysis_for_user(session["user_id"]) | |
| if not latest: | |
| flash("No reports found yet.", "info") | |
| return redirect(url_for("dashboard")) | |
| return redirect(url_for("report_view", analysis_id=latest["id"])) | |
| def report_view(analysis_id: int): | |
| analysis = get_analysis_by_id(analysis_id, user_id=session["user_id"]) | |
| if not analysis: | |
| abort(404) | |
| payload = analysis.get("payload_json") or {} | |
| response = payload.get("response", {}) if isinstance(payload, dict) else {} | |
| final_state = payload.get("final_state", {}) if isinstance(payload, dict) else {} | |
| return render_template( | |
| "report.html", | |
| analysis=analysis, | |
| response=response, | |
| final_state=final_state, | |
| ) | |
| def download_report_json(analysis_id: int): | |
| analysis = get_analysis_by_id(analysis_id, user_id=session["user_id"]) | |
| if not analysis: | |
| abort(404) | |
| payload = analysis.get("payload_json") or {} | |
| return jsonify({"analysis": analysis, "payload": payload}) | |
| def download_report_markdown(analysis_id: int): | |
| analysis = get_analysis_by_id(analysis_id, user_id=session["user_id"]) | |
| if not analysis: | |
| abort(404) | |
| markdown = analysis.get("final_report") or "" | |
| filename = f"aispy-report-{analysis_id}.md" | |
| return Response( | |
| markdown, | |
| mimetype="text/markdown; charset=utf-8", | |
| headers={"Content-Disposition": f'attachment; filename="{filename}"'}, | |
| ) | |
| def api_report(analysis_id: int): | |
| analysis = get_analysis_by_id(analysis_id, user_id=session["user_id"]) | |
| if not analysis: | |
| return jsonify({"error": "Report not found."}), 404 | |
| return jsonify(analysis) | |
| if __name__ == "__main__": | |
| app.run(debug=True, port=int(os.getenv("PORT", "5000"))) |