face-intel / models /reports.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
14.1 kB
"""Report domain models — the unified output of a face-intelligence job."""
from __future__ import annotations
import uuid
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class Evidence(BaseModel):
"""Preserved raw provider output — never discarded.
Every result from every provider is preserved as Evidence, regardless
of success. This is the foundation of evidence-first design.
"""
provider: str
capability: str
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
raw: Any = None # verbatim provider response
normalized: dict = Field(default_factory=dict)
elapsed_ms: float = 0.0
success: bool = True
error: Optional[str] = None
error_type: Optional[str] = None
metadata: dict = Field(default_factory=dict)
retry_count: int = 0
limitations: List[str] = Field(default_factory=list)
class ConfidenceScore(BaseModel):
"""Explainable confidence — decomposed into weighted sub-scores."""
overall: float # 0.0 – 1.0
components: Dict[str, float] = Field(default_factory=dict)
explanation: str = ""
method: str = "weighted_average"
class FaceDetection(BaseModel):
"""One detected face, with cross-provider consensus."""
box: Dict[str, int] # {x, y, w, h}
confidence: ConfidenceScore
landmarks: Optional[Dict[str, List[int]]] = None
detected_by: List[str] = Field(default_factory=list)
embedding: Optional[List[float]] = None
embedding_provider: Optional[str] = None
class FaceMatch(BaseModel):
"""A recognition match against the reference gallery."""
query_face_index: int
best_match: Optional[str] = None
confidence: ConfidenceScore
distances: Dict[str, float] = Field(default_factory=dict)
class ImageAnalysisResult(BaseModel):
"""Output of an image-analysis provider (quality, properties, features)."""
provider: str
quality_score: Optional[float] = None
brightness: Optional[float] = None
contrast: Optional[float] = None
sharpness: Optional[float] = None
noise_level: Optional[float] = None
width: Optional[int] = None
height: Optional[int] = None
channels: Optional[int] = None
color_profile: Optional[str] = None
dominant_colors: List[str] = Field(default_factory=list)
aspects: dict = Field(default_factory=dict)
confidence: Optional[ConfidenceScore] = None
class MetadataResult(BaseModel):
"""Output of a metadata-extraction provider (EXIF, XMP, IPTC)."""
provider: str
format: Optional[str] = None
exif: dict = Field(default_factory=dict)
xmp: dict = Field(default_factory=dict)
iptc: dict = Field(default_factory=dict)
gps: Optional[dict] = None
camera_make: Optional[str] = None
camera_model: Optional[str] = None
software: Optional[str] = None
capture_time: Optional[str] = None
confidence: Optional[ConfidenceScore] = None
class ForensicsResult(BaseModel):
"""Output of a forensics provider (integrity, duplicates, manipulation)."""
provider: str
integrity_score: Optional[float] = None
is_duplicate: Optional[bool] = None
duplicate_of: Optional[str] = None
similarity_score: Optional[float] = None
manipulation_indicators: List[str] = Field(default_factory=list)
ela_score: Optional[float] = None # error-level analysis
noise_inconsistency: Optional[float] = None
details: dict = Field(default_factory=dict)
confidence: Optional[ConfidenceScore] = None
class OCRResult(BaseModel):
"""Output of an OCR provider."""
provider: str
text_blocks: List[dict] = Field(default_factory=list)
full_text: str = ""
language: Optional[str] = None
confidence: Optional[ConfidenceScore] = None
class ObjectDetectionResult(BaseModel):
"""Output of an object-detection provider."""
provider: str
objects: List[dict] = Field(default_factory=list)
model: Optional[str] = None
confidence: Optional[ConfidenceScore] = None
class SceneResult(BaseModel):
"""Output of a scene-classification provider."""
provider: str
labels: List[dict] = Field(default_factory=list)
confidence: Optional[ConfidenceScore] = None
class NSFWResult(BaseModel):
"""Output of an NSFW-detection provider."""
provider: str
is_nsfw: bool = False
labels: List[str] = Field(default_factory=list)
confidence: Optional[ConfidenceScore] = None
class AIDetectionResult(BaseModel):
"""Output of an AI-generated-image-detection provider."""
provider: str
is_ai_generated: bool = False
confidence: Optional[ConfidenceScore] = None
class EmbeddingResult(BaseModel):
"""Output of an embedding provider."""
provider: str
embedding: List[float] = Field(default_factory=list)
model: Optional[str] = None
dimensions: int = 0
# --------------------------------------------------------------------------- #
# OSINT models (Phase 10-15)
# --------------------------------------------------------------------------- #
class OSINTMatch(BaseModel):
"""One merged reverse-image-search match with source classification."""
image_url: str = ""
source_page: str = ""
title: str = ""
snippet: str = ""
thumbnail: str = ""
source_type: str = "unknown"
platform: Optional[str] = None
root_domain: str = ""
is_cdn: bool = False
confidence: float = 0.0
first_seen: Optional[str] = None
found_by: List[str] = Field(default_factory=list)
class OSINTResult(BaseModel):
"""Output of the OSINT reverse-search orchestrator."""
providers_invoked: List[str] = Field(default_factory=list)
providers_succeeded: List[str] = Field(default_factory=list)
providers_failed: List[str] = Field(default_factory=list)
total_matches: int = 0
matches: List[OSINTMatch] = Field(default_factory=list)
source_type_breakdown: dict = Field(default_factory=dict)
elapsed_ms: float = 0.0
class FaceQualityMetrics(BaseModel):
"""Quality metrics for a single detected face."""
quality_score: float = 0.0
blur_score: float = 0.0
is_blurry: bool = False
face_size: int = 0 # width * height of bbox
face_size_label: str = "" # "small" | "medium" | "large"
pose_yaw: float = 0.0 # degrees; 0 = frontal
pose_pitch: float = 0.0
pose_roll: float = 0.0
pose_label: str = "" # "frontal" | "profile" | "extreme"
orientation: str = "" # "upright" | "tilted" | "rotated"
is_best_face: bool = False
class FaceCluster(BaseModel):
"""A cluster of faces that appear to be the same person."""
cluster_id: int
face_indices: List[int] = Field(default_factory=list)
representative_index: int = 0
num_faces: int = 0
avg_similarity: float = 0.0
class FaceIntelligenceResult(BaseModel):
"""Output of the face intelligence service."""
total_faces: int = 0
best_face_index: Optional[int] = None
quality_metrics: List[FaceQualityMetrics] = Field(default_factory=list)
clusters: List[FaceCluster] = Field(default_factory=list)
duplicate_face_indices: List[int] = Field(default_factory=list)
elapsed_ms: float = 0.0
class ForensicMetadataReport(BaseModel):
"""Forensic metadata intelligence — expanded EXIF/XMP/IPTC/ICC."""
provider: str = "forensic_metadata"
format: Optional[str] = None
exif: dict = Field(default_factory=dict)
xmp: dict = Field(default_factory=dict)
iptc: dict = Field(default_factory=dict)
icc_profile: Optional[dict] = None
gps: Optional[dict] = None
camera_make: Optional[str] = None
camera_model: Optional[str] = None
lens_model: Optional[str] = None
software: Optional[str] = None
capture_time: Optional[str] = None
capture_time_iso: Optional[str] = None
timezone_estimate: Optional[str] = None
editing_history: List[str] = Field(default_factory=list)
thumbnail_extracted: bool = False
embedded_preview: bool = False
camera_fingerprint: Optional[str] = None # hash of camera-specific noise pattern
compression_analysis: Optional[dict] = None
confidence: Optional[ConfidenceScore] = None
class DetectedObjectIntelligence(BaseModel):
"""One detected object with intelligence metadata."""
class_label: str
confidence: float = 0.0
box: dict = Field(default_factory=dict)
crop_base64: Optional[str] = None
is_vehicle: bool = False
is_screen: bool = False
is_document: bool = False
is_phone: bool = False
is_laptop: bool = False
is_watch: bool = False
is_logo: bool = False
is_text_region: bool = False
is_license_plate: bool = False
is_qr_code: bool = False
is_barcode: bool = False
searchable_metadata: dict = Field(default_factory=dict)
class ObjectIntelligenceResult(BaseModel):
"""Output of the object intelligence service."""
total_objects: int = 0
objects: List[DetectedObjectIntelligence] = Field(default_factory=list)
vehicles: List[DetectedObjectIntelligence] = Field(default_factory=list)
license_plates: List[DetectedObjectIntelligence] = Field(default_factory=list)
qr_codes: List[DetectedObjectIntelligence] = Field(default_factory=list)
barcodes: List[DetectedObjectIntelligence] = Field(default_factory=list)
documents: List[DetectedObjectIntelligence] = Field(default_factory=list)
screens: List[DetectedObjectIntelligence] = Field(default_factory=list)
phones: List[DetectedObjectIntelligence] = Field(default_factory=list)
laptops: List[DetectedObjectIntelligence] = Field(default_factory=list)
watches: List[DetectedObjectIntelligence] = Field(default_factory=list)
logos: List[DetectedObjectIntelligence] = Field(default_factory=list)
text_regions: List[DetectedObjectIntelligence] = Field(default_factory=list)
elapsed_ms: float = 0.0
class LocationEvidence(BaseModel):
"""One piece of evidence for a location estimate."""
source: str = "" # "gps" | "ocr" | "language" | "scene" | "license_plate" | "logo"
value: str = ""
confidence: float = 0.0
details: dict = Field(default_factory=dict)
class LocationEstimate(BaseModel):
"""Output of the location intelligence service."""
candidate_countries: List[dict] = Field(default_factory=list) # [{"country": "France", "confidence": 0.8}]
candidate_cities: List[dict] = Field(default_factory=list)
gps: Optional[dict] = None
evidence: List[LocationEvidence] = Field(default_factory=list)
conflicting_evidence: List[str] = Field(default_factory=list)
overall_confidence: float = 0.0
elapsed_ms: float = 0.0
class CorrelationNode(BaseModel):
"""A node in the correlation graph."""
id: str
node_type: str # "face" | "object" | "location" | "metadata" | "image" | "embedding"
label: str = ""
properties: dict = Field(default_factory=dict)
class CorrelationEdge(BaseModel):
"""An edge in the correlation graph — a deterministic relationship."""
source: str
target: str
edge_type: str # "same_face" | "same_object" | "same_location" | "same_camera" | "same_hash" | "same_embedding" | "same_metadata" | "same_timestamp"
confidence: float = 1.0
evidence: str = ""
class CorrelationGraph(BaseModel):
"""Output of the correlation engine."""
nodes: List[CorrelationNode] = Field(default_factory=list)
edges: List[CorrelationEdge] = Field(default_factory=list)
num_nodes: int = 0
num_edges: int = 0
elapsed_ms: float = 0.0
class ConflictReport(BaseModel):
"""Cross-provider disagreement."""
kind: str # e.g. "face_count_mismatch"
providers: List[str]
description: str
severity: str = "info" # info | warning | error
class ReportMetadata(BaseModel):
"""Job/report metadata."""
job_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
image_hash: Optional[str] = None
total_elapsed_ms: float = 0.0
providers_invoked: List[str] = Field(default_factory=list)
providers_succeeded: List[str] = Field(default_factory=list)
providers_failed: List[str] = Field(default_factory=list)
limitations: List[str] = Field(default_factory=list)
class UnifiedFaceReport(BaseModel):
"""The final unified report consumed by API + UI."""
metadata: ReportMetadata
detections: List[FaceDetection] = Field(default_factory=list)
matches: List[FaceMatch] = Field(default_factory=list)
scraped_images: List[dict] = Field(default_factory=list)
reverse_matches: List[dict] = Field(default_factory=list)
image_analyses: List[ImageAnalysisResult] = Field(default_factory=list)
metadata_extractions: List[MetadataResult] = Field(default_factory=list)
forensics: List[ForensicsResult] = Field(default_factory=list)
ocr_results: List[OCRResult] = Field(default_factory=list)
object_detections: List[ObjectDetectionResult] = Field(default_factory=list)
scene_results: List[SceneResult] = Field(default_factory=list)
nsfw_results: List[NSFWResult] = Field(default_factory=list)
ai_detection_results: List[AIDetectionResult] = Field(default_factory=list)
embedding_results: List[EmbeddingResult] = Field(default_factory=list)
# OSINT (Phase 10-15)
osint: Optional[OSINTResult] = None
face_intelligence: Optional[FaceIntelligenceResult] = None
forensic_metadata: Optional[ForensicMetadataReport] = None
object_intelligence: Optional[ObjectIntelligenceResult] = None
location_estimate: Optional[LocationEstimate] = None
correlation_graph: Optional[CorrelationGraph] = None
evidence: List[Evidence] = Field(default_factory=list)
conflicts: List[ConflictReport] = Field(default_factory=list)
overall_confidence: Optional[ConfidenceScore] = None