from fastapi import FastAPI, HTTPException, Depends from core.schema import OptimizationRequest, OptimizationResult from main import QuantScaleSystem import logging app = FastAPI(title="QuantScale AI API", version="1.0.0") logger = logging.getLogger("API") # Singleton System system = QuantScaleSystem() from fastapi.responses import RedirectResponse from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles # Mount static files app.mount("/static", StaticFiles(directory="api/static"), name="static") @app.get("/") def root(): """Serves the AI Interface.""" return FileResponse('api/static/index.html') @app.get("/health") def health_check(): return {"status": "healthy", "service": "QuantScale AI Direct Indexing"} def parse_constraints_with_llm(user_prompt: str) -> list: """ Dedicated parser function in the API layer. Maps natural language to exact GICS sectors. """ return system.ai_reporter.parse_intent(user_prompt) @app.post("/optimize", response_model=dict) def optimize_portfolio(request: OptimizationRequest): """ Optimizes a portfolio based on exclusions and generates an AI Attribution report. """ try: # If the request contains a raw prompt but no sectors, parse it here if request.user_prompt and not request.excluded_sectors: request.excluded_sectors = parse_constraints_with_llm(request.user_prompt) result = system.run_pipeline(request) if not result: raise HTTPException(status_code=500, detail="Pipeline failed to execute.") return { "client_id": request.client_id, "allocations": result['optimization'].weights, "tracking_error": result['optimization'].tracking_error, "attribution_narrative": result['commentary'] } except Exception as e: logger.error(f"API Error: {e}") raise HTTPException(status_code=500, detail=str(e))