from fastapi import FastAPI, Depends, HTTPException, BackgroundTasks from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from sqlalchemy.orm import Session from sqlalchemy import desc import json import os from backend.db import database, models, schemas from backend.logic.deterministic import process_pipeline from backend.services.llm_advisor import generate_explanation from backend.services.hf_sync import sync_db_to_hf # Create tables models.Base.metadata.create_all(bind=database.engine) app = FastAPI(title="Loan Prediction System API") app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allow all for demo allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.post("/api/predict", response_model=schemas.LoanApplicationResponse) def create_prediction(app_in: schemas.LoanApplicationCreate, background_tasks: BackgroundTasks, db: Session = Depends(database.get_db)): # 1. Convert to dictionary input_data = app_in.model_dump() # 2. Run Deterministic Layer (Prediction + Wait-If Simulation) packet = process_pipeline(input_data) # 3. Pass Packet to LLM Layer explanation = generate_explanation(packet) # 4. Save to DB db_record = models.LoanApplication( applicant_name=app_in.applicant_name, gender=app_in.gender, married=app_in.married, dependents=app_in.dependents, education=app_in.education, self_employed=app_in.self_employed, applicant_income=app_in.applicant_income, coapplicant_income=app_in.coapplicant_income, loan_amount=app_in.loan_amount, loan_amount_term=app_in.loan_amount_term, credit_history=app_in.credit_history, property_area=app_in.property_area, prediction=packet["prediction"], confidence=packet["confidence"], dti_ratio=packet["dti_ratio"], explanation_text=explanation, optimized_suggestion=packet["optimized_suggestion"], feature_importance_json=json.dumps(packet["feature_importances"]), benchmarks_json=json.dumps(packet["benchmarks"]) ) db.add(db_record) db.commit() db.refresh(db_record) # Trigger HF Backup in background background_tasks.add_task(sync_db_to_hf) return db_record @app.get("/api/history", response_model=list[schemas.LoanApplicationResponse]) def get_history(limit: int = 10, db: Session = Depends(database.get_db)): records = db.query(models.LoanApplication).order_by(desc(models.LoanApplication.created_at)).limit(limit).all() return records @app.delete("/api/history") def clear_history(background_tasks: BackgroundTasks, db: Session = Depends(database.get_db)): db.query(models.LoanApplication).delete() db.commit() # Trigger HF Backup in background background_tasks.add_task(sync_db_to_hf) return {"message": "History cleared successfully"} # Serve Frontend frontend_build_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist") if os.path.isdir(frontend_build_path): app.mount("/assets", StaticFiles(directory=os.path.join(frontend_build_path, "assets")), name="assets") @app.get("/{full_path:path}") def catch_all(full_path: str): index_path = os.path.join(frontend_build_path, "index.html") if os.path.isfile(index_path): return FileResponse(index_path) return {"error": "Frontend build not found."}