from fastapi import FastAPI, HTTPException, BackgroundTasks from pydantic import BaseModel, HttpUrl from typing import Optional, Dict, List, Any import logging import time from src.core.pipeline import Pipeline from src.ml import slm_explainer # Configure Logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger("api") # Initialize App app = FastAPI( title="PhishingInsight API", description="AI-powered Phishing Detection & URL Analysis API", version="1.0.0" ) # Initialize Pipeline (Global Singleton) logger.info("API: Initializing Global Pipeline...") pipeline = Pipeline() # Load SLM in background on startup @app.on_event("startup") async def startup_event(): logger.info("API: Pre-loading SLM Model...") # We do this asynchronously or threading to not block, # but slm_explainer.load_model() blocking is safer for "ready" state. # For now, we rely on the main process having loaded it previously # or it loading on first request if isolated. try: slm_explainer.load_model() logger.info("API: SLM Model Loaded.") except Exception as e: logger.error(f"API: Model load warning: {e}") # --- Data Models --- class URLRequest(BaseModel): url: str class SignalScores(BaseModel): url_analysis: float domain_check: float class AnalysisResponse(BaseModel): success: bool url: str verdict: str # SAFE, WARNING, DANGER score: float # 0.0 to 1.0 (Risk) is_phishing: bool summary: str explanation: str advice: str signals: SignalScores findings: List[str] timestamp: str time_ms: int error: Optional[str] = None # --- Endpoints --- @app.get("/health") def health_check(): """Health check endpoint for Docker/Kubernetes.""" return {"status": "healthy", "service": "PhishingInsight-API"} @app.get("/version") def version_info(): """Return API and Model versions.""" return { "api_version": "1.0.0", "model_type": "Hybrid (LightGBM + TinyLlama)" } @app.post("/analyze", response_model=AnalysisResponse) def analyze_url(request: URLRequest): """ Analyze a URL for phishing threats. """ logger.info(f"API Request: Analyze {request.url}") result = pipeline.analyze(request.url) if not result.get('success'): logger.warning(f"API Analysis Failed: {result.get('error')}") # Even if logical failure (invalid URL), we return a structured response with error return { "success": False, "url": request.url, "verdict": "ERROR", "score": 0.0, "is_phishing": False, "summary": "Analysis Failed", "explanation": result.get('error', "Unknown error"), "advice": "Check the URL and try again.", "signals": {"url_analysis": 0, "domain_check": 0}, "findings": [], "timestamp": "", "time_ms": 0, "error": result.get('error') } return result