from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import uvicorn import os from dotenv import load_dotenv from typing import Any, Dict load_dotenv() # Local module imports (we'll implement these next) from backend import ai_agent from backend import expense_manager from backend import budget_allocator from backend import reminder_system # App init app = FastAPI(title="FinSync AI Backend") # Allow CORS from Streamlit / local dev app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Pydantic models class AnalyzeRequest(BaseModel): user_id: str text: str class ExpenseRequest(BaseModel): user_id: str name: str amount: float category: str = "" date: str = "" class ReminderRequest(BaseModel): user_id: str name: str date: str amount: float = 0.0 notes: str = "" # Simple health check @app.get("/health") async def health(): return {"status": "ok", "service": "finsync-backend"} # Analyze / natural language endpoint @app.post("/analyze") async def analyze(req: AnalyzeRequest): """ Send user natural command to AI agent; AI responds with action/intention and human-readable reply. Example use-cases: - "Show my expenses this month" - "Set reminder to pay rent on 2025-11-10" - "Add expense Rent 500" """ try: response: Dict[str, Any] = ai_agent.parse_and_respond(user_id=req.user_id, text=req.text) return {"ok": True, "result": response} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # Add expense endpoint @app.post("/add_expense") async def add_expense(req: ExpenseRequest): """ Add an expense into the database.json (demo DB). """ try: entry = { "user_id": req.user_id, "name": req.name, "amount": float(req.amount), "category": req.category or "Uncategorized", "date": req.date or expense_manager.today_iso(), } expense_manager.add_expense(entry) return {"ok": True, "expense": entry} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # List expenses for a user @app.get("/expenses") async def get_expenses(user_id: str): try: items = expense_manager.list_expenses(user_id=user_id) return {"ok": True, "expenses": items} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # Get budget suggestion @app.get("/budget") async def get_budget(user_id: str): try: expenses = expense_manager.list_expenses(user_id=user_id) suggestion = budget_allocator.suggest_budget(user_id=user_id, expenses=expenses) return {"ok": True, "budget": suggestion} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # Add reminder @app.post("/reminder") async def add_reminder(req: ReminderRequest): try: reminder = { "user_id": req.user_id, "name": req.name, "date": req.date, "amount": float(req.amount), "notes": req.notes } reminder_system.add_reminder(reminder) return {"ok": True, "reminder": reminder} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # List reminders @app.get("/reminders") async def list_reminders(user_id: str): try: items = reminder_system.list_reminders(user_id=user_id) return {"ok": True, "reminders": items} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": port = int(os.environ.get("BACKEND_PORT", 8000)) uvicorn.run("backend.main:app", host="0.0.0.0", port=port, reload=True)