deepshield-api / backend /core /config.py
vanshpatil16's picture
DeepShield API β€” 7-detector image deepfake detection
6be0044
Raw
History Blame Contribute Delete
8.58 kB
"""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"
@property
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()