Spaces:
Sleeping
Sleeping
| 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 | |
| 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 --- | |
| def health_check(): | |
| """Health check endpoint for Docker/Kubernetes.""" | |
| return {"status": "healthy", "service": "PhishingInsight-API"} | |
| def version_info(): | |
| """Return API and Model versions.""" | |
| return { | |
| "api_version": "1.0.0", | |
| "model_type": "Hybrid (LightGBM + TinyLlama)" | |
| } | |
| 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 | |