Spaces:
Running
Running
| """Central configuration β loaded once at startup from .env.""" | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import List, Tuple | |
| from pydantic_settings import BaseSettings, SettingsConfigDict | |
| # Project root is three levels up from this file (backend/core/config.py) | |
| _PROJECT_ROOT = Path(__file__).parent.parent.parent | |
| class Settings(BaseSettings): | |
| model_config = SettingsConfigDict( | |
| env_file=_PROJECT_ROOT / ".env", | |
| env_file_encoding="utf-8", | |
| extra="ignore", | |
| ) | |
| # ββ Specialized AI detection APIs βββββββββββββββββββββββββββ | |
| hive_api_key: str = "" | |
| hive_vlm_secret_key: str = "" # Hive VLM (Bearer token) | |
| aiornot_api_key: str = "" | |
| reality_defender_api_key: str = "" | |
| # Support multiple Sightengine keys for failover (comma-separated) | |
| sightengine_api_users: str = "" # e.g., "user1,user2,user3" | |
| sightengine_api_secrets: str = "" # e.g., "secret1,secret2,secret3" | |
| # Backward compatibility: single key (if provided, converted to list with 1 element) | |
| sightengine_api_user: str = "" | |
| sightengine_api_secret: str = "" | |
| # ββ VLM APIs ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| anthropic_api_key: str = "" | |
| openai_api_key: str = "" | |
| google_api_key: str = "" | |
| open_route_api_key: str = "" | |
| mistral_api_key: str = "" | |
| # ββ Detector toggles ββββββββββββββββββββββββββββββββββββββββ | |
| enable_hive: bool = False | |
| enable_hive_vlm: bool = False | |
| enable_aiornot: bool = False | |
| enable_sightengine: bool = True | |
| enable_reality_defender: bool = False | |
| enable_claude: bool = False | |
| enable_gpt4: bool = False | |
| enable_gemini: bool = False | |
| enable_local_forensics: bool = False | |
| enable_hybrid_model: bool = False | |
| enable_openrouter_gemma: bool = False | |
| enable_openrouter_gpt4: bool = False | |
| enable_openrouter_llama: bool = False | |
| enable_openrouter_mistral: bool = False | |
| enable_mistral: bool = False # Mistral direct API | |
| enable_local_finetuned: bool = True # Fine-tuned Swin Transformer (92% AI recall) | |
| # ββ V3 detector toggles βββββββββββββββββββββββββββββββββββββ | |
| enable_frequency_domain: bool = True # FFT spectrum ResNet-18 (Step 2) | |
| enable_clip_unifd: bool = True # CLIP ViT-L/14 linear head (Step 3, needs open_clip) | |
| enable_dire: bool = True # Diffusion reconstruction error (Step 4 β vae_proxy on CPU, full_dire on GPU) | |
| enable_face_specialist: bool = False # F3-Net face forensics (Step 6) | |
| enable_siglip2_aigc: bool = True # SigLIP2-base open-deepfake-detection (Nov 2025) | |
| enable_npr: bool = True # NPR ResNet-50 upsampling artifact detector (CVPR 2024) | |
| use_meta_learner_fusion: bool = False # XGBoost/LR meta-learner (Step 5; False = Bayesian log-odds fallback) | |
| # fusion_strategy: "weighted" (Bayesian log-odds) | "voting" (majority vote, each detector 1 vote) | |
| fusion_strategy: str = "weighted" | |
| # Voting threshold: fraction of detectors that must vote AI to return AI_GENERATED (default 0.5 = majority) | |
| voting_threshold: float = 0.5 | |
| # ββ V1 HybridForensicsModel paths βββββββββββββββββββββββββββ | |
| # Path to the trained checkpoint file (hybrid_detector.pt) | |
| hybrid_model_checkpoint: str = str( | |
| _PROJECT_ROOT.parent / "Deepfake-V1" / "Professional_Integration" | |
| / "ml" / "checkpoints" / "hybrid_detector.pt" | |
| ) | |
| # Path to Professional_Integration/ so `import ml` works | |
| hybrid_model_code_root: str = str( | |
| _PROJECT_ROOT.parent / "Deepfake-V1" / "Professional_Integration" | |
| ) | |
| # Model versions: latest and backup | |
| hybrid_model_latest_checkpoint: str = str( | |
| _PROJECT_ROOT.parent / "Deepfake-V1" / "Professional_Integration" | |
| / "ml" / "checkpoints" / "hybrid_detector.pt" | |
| ) | |
| hybrid_model_backup_checkpoint: str = str( | |
| _PROJECT_ROOT.parent / "Deepfake-V1" / "Professional_Integration" | |
| / "ml" / "checkpoints" / "hybrid_detector_v1_backup.pt" | |
| ) | |
| # Default model version to use (latest or backup) | |
| hybrid_model_default_version: str = "latest" | |
| # ββ Fusion weights (sum need not equal 1 β normalised at runtime) ββ | |
| # Higher = more trusted. Tune after benchmarking. | |
| weight_hive: float = 2.5 # purpose-built, high accuracy | |
| weight_hive_vlm: float = 2.0 # Hive VLM β strong vision reasoning | |
| weight_aiornot: float = 2.0 # diverse training set | |
| weight_sightengine: float = 3.0 # primary signal | |
| weight_mistral: float = 2.2 # direct Mistral API β strong multimodal | |
| weight_claude: float = 2.2 # Anthropic VLM β strong reasoning | |
| weight_gpt4: float = 1.5 | |
| weight_gemini: float = 0.4 # low weight: Gemini Vision mislabels its own generated images as real | |
| weight_gemma_vision: float = 1.3 | |
| weight_openrouter_gpt4: float = 2.0 | |
| weight_openrouter_llama: float = 1.5 | |
| weight_openrouter_mistral: float = 1.4 | |
| weight_local_forensics: float = 0.6 # supporting signal only | |
| weight_local_finetuned: float = 2.5 # fine-tuned Swin: 92% AI recall on domain data | |
| weight_hybrid_model: float = 1.5 | |
| # V3 detector weights | |
| weight_frequency_domain: float = 1.5 # trained ResNet-18 on FFT spectra | |
| weight_clip_unifd: float = 1.2 # UniFD linear head β can FP on compressed images | |
| weight_dire: float = 1.5 # DIRE reconstruction error (Step 4) | |
| weight_face_specialist: float = 0.5 # unreliable on non-manipulation AI images | |
| weight_siglip2_aigc: float = 2.0 # SigLIP2 Nov 2025 β strong on diffusion + AI fashion | |
| weight_npr: float = 1.8 # NPR CVPR 2024 β upsampling artifacts, fast CPU | |
| # ββ Confidence-based gating thresholds ββββββββββββββββββββββ | |
| # Used to skip expensive detectors if cheap ones are already confident | |
| enable_gating: bool = True | |
| gating_confidence_threshold: float = 0.6 # confidence needed to skip Tier 2+ | |
| gating_p_fake_margin: float = 0.2 # |p_fake - 0.5| margin for early exit | |
| # ββ Verdict thresholds ββββββββββββββββββββββββββββββββββββββ | |
| # Calibrated for accuracy on modern diffusion images (Apr 20): | |
| # Lower ai_threshold so fused log-odds actually reaches it; | |
| # raise real_threshold so weak REAL signals don't mask AI images. | |
| ai_threshold: float = 0.42 # lowered: Sightengine alone can push past this | |
| real_threshold: float = 0.28 # real images cluster near 0.02-0.04 so safe to lower | |
| # between thresholds β UNCERTAIN | |
| # ββ HTTP ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| request_timeout: float = 20.0 # seconds per API call | |
| # ββ MongoDB ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| mongodb_url: str = "mongodb://localhost:27017" | |
| mongodb_database: str = "deepshield" | |
| def sightengine_pairs(self) -> List[Tuple[str, str]]: | |
| """Parse Sightengine credentials into (user, secret) pairs for failover. | |
| Supports both: | |
| - Multiple keys: SIGHTENGINE_API_USERS=user1,user2 SIGHTENGINE_API_SECRETS=secret1,secret2 | |
| - Single key (backward compat): SIGHTENGINE_API_USER=user SIGHTENGINE_API_SECRET=secret | |
| """ | |
| # Try multi-key format first | |
| if self.sightengine_api_users and self.sightengine_api_secrets: | |
| users = [u.strip() for u in self.sightengine_api_users.split(",") if u.strip()] | |
| secrets = [s.strip() for s in self.sightengine_api_secrets.split(",") if s.strip()] | |
| pairs = list(zip(users, secrets)) | |
| if pairs: | |
| return pairs | |
| # Fall back to single-key format | |
| if self.sightengine_api_user and self.sightengine_api_secret: | |
| return [(self.sightengine_api_user, self.sightengine_api_secret)] | |
| return [] | |
| settings = Settings() | |