"""Application factory for the Flask service.""" from __future__ import annotations from pathlib import Path from time import perf_counter from flask import Flask, Response, g, jsonify, request from flask_cors import CORS from werkzeug.exceptions import HTTPException from api_layer.routes import build_routes from brain.decision_maker import DecisionEngine from config.settings import AppSettings, configure_logging, load_settings from core_engine.analytics import AnalyticsTracker from core_engine.leaderboard import LeaderboardStore from core_engine.simulator import SimulationEngine from utils.logger import get_logger LOGGER = get_logger(__name__) def create_server(settings: AppSettings | None = None) -> Flask: """Create and configure the Flask application.""" app_settings = settings or load_settings() configure_logging(app_settings) frontend_dir = Path(__file__).resolve().parent.parent / "frontend" app = Flask( __name__, static_folder=str(frontend_dir), static_url_path="", ) CORS(app, resources={r"/*": {"origins": app_settings.cors_origins}}) engine = SimulationEngine( batch_size=app_settings.default_mail_count, random_seed=app_settings.random_seed, simulation_mode=app_settings.simulation_mode, ) decision_engine = DecisionEngine( model_type=app_settings.model_type, hf_model=app_settings.hf_model, hf_token=app_settings.hf_token, hf_timeout=app_settings.hf_timeout, ) analytics = AnalyticsTracker() leaderboard = LeaderboardStore() @app.before_request def log_request_start() -> None: g.request_started_at = perf_counter() @app.after_request def log_request_end(response: Response) -> Response: started_at = getattr(g, "request_started_at", perf_counter()) elapsed_ms = (perf_counter() - started_at) * 1000 LOGGER.info( "%s %s -> %s %.2fms", request.method, request.path, response.status_code, elapsed_ms, ) return response @app.get("/") def dashboard() -> object: try: return app.send_static_file("index.html") except Exception: LOGGER.exception("Failed to serve dashboard.") return jsonify({"success": False, "error": "Unable to serve dashboard."}), 500 @app.errorhandler(HTTPException) def handle_http_error(error: HTTPException) -> tuple[Response, int]: return ( jsonify( { "success": False, "error": error.description or "HTTP request failed.", } ), error.code or 500, ) @app.errorhandler(Exception) def handle_unexpected_error(error: Exception) -> tuple[Response, int]: LOGGER.exception("Unhandled application error: %s", error) return jsonify({"success": False, "error": "Internal server error."}), 500 app.register_blueprint( build_routes( engine, decision_engine=decision_engine, analytics=analytics, leaderboard=leaderboard, max_email_count=app_settings.max_emails, ) ) app.config["simulation_engine"] = engine app.config["decision_engine"] = decision_engine app.config["analytics"] = analytics app.config["leaderboard"] = leaderboard return app