Spaces:
Sleeping
Sleeping
| import math | |
| import os | |
| import sys | |
| from pydantic import BaseModel, Field | |
| # ── Import your existing cli.py functions directly ─────────────────────────── | |
| try: | |
| from cli import clean_text, predict, load_models as cli_load_models | |
| except ImportError: | |
| # Fallback to handle relative imports if invoked directly by master gateway | |
| from Version_3.cli import clean_text, predict, load_models as cli_load_models | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # 1. REQUIRED SCHEMAS FOR MASTER GATEWAY COMPATIBILITY | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class EmailScanRequest(BaseModel): | |
| """ | |
| Must use 'email_body' to match app.js and the validation expected by main.py | |
| """ | |
| email_body: str = Field(..., description="The raw email text or headers to scan") | |
| class EmailScanResponse(BaseModel): | |
| """ | |
| Must match the exact structure enforced by response_model in master main.py | |
| """ | |
| verdict: str | |
| confidence: float | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # 2. REQUIRED GLOBAL CONTEXT VARIABLES | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| _vectorizer = None | |
| _model = None | |
| _load_error = None | |
| def load_models(): | |
| """ | |
| Called automatically by the master gateway bootstrapper. | |
| """ | |
| global _vectorizer, _model, _load_error | |
| try: | |
| _vectorizer, _model = cli_load_models() | |
| print("[Chimera V3] SVM Models loaded successfully via master handshake.") | |
| return _vectorizer, _model | |
| except SystemExit: | |
| _load_error = "Model files not found. Check models/SVM_model.pkl and models/vectorizer.pkl exist." | |
| print(f"[Chimera V3] WARNING: {_load_error}") | |
| except Exception as e: | |
| _load_error = str(e) | |
| print(f"[Chimera V3] WARNING: Could not load models — {_load_error}") | |
| return None, None | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # 3. CORE ROUTING EXECUTION ENTRYPOINT | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def scan_email(payload: EmailScanRequest) -> EmailScanResponse: | |
| """ | |
| Direct functional endpoint executed by the master route handler. | |
| """ | |
| global _model, _vectorizer, _load_error | |
| # Lazy loading safe-state fallback for mounted instances | |
| if _model is None or _vectorizer is None: | |
| _vectorizer, _model = load_models() | |
| # If models are genuinely unavailable, fallback gracefully | |
| if _model is None or _vectorizer is None: | |
| return EmailScanResponse( | |
| verdict="Suspicious (Model Offline)", | |
| confidence=0.50 | |
| ) | |
| text_to_analyze = payload.email_body | |
| # Guard: empty body text check | |
| if not text_to_analyze.strip(): | |
| return EmailScanResponse(verdict="Clean", confidence=0.0) | |
| # ── Run your underlying SVM model prediction ───────────────────────────── | |
| # Emulating a blank display_name context to feed into your cli pipeline | |
| combined_text = f"\n{text_to_analyze}" | |
| label, confidence, spam_score = predict(combined_text, _vectorizer, _model) | |
| # Calculate normalized confidence float (0.0 to 1.0) | |
| risk_score = spam_score if spam_score is not None else (100.0 if label == "Spam" else 0.0) | |
| final_confidence = (confidence / 100.0) if (confidence is not None) else (risk_score / 100.0) | |
| # ── Map results to match standard frontend badge expectation ────────────── | |
| if risk_score >= 70: | |
| assigned_verdict = "phishing" | |
| elif risk_score >= 40: | |
| assigned_verdict = "suspicious" | |
| else: | |
| assigned_verdict = "clean" | |
| return EmailScanResponse( | |
| verdict=assigned_verdict, | |
| confidence=final_confidence | |
| ) |