scanner / src /deepfake_scanner /api /schemas.py
github-actions[bot]
Deploy from GitHub 39b3777315c11d9c8bcd39ad7bf034f2a88a7379 (filtered: code + Dockerfile + README + NOTICES only)
2e175db
Raw
History Blame Contribute Delete
2.81 kB
"""
API schemas (v1).
These are the public contract for the /v1/scan/image endpoint. Treat any change
to a field name, type, or required-ness as a breaking change.
Forward-compatibility notes
---------------------------
• `probabilities` is a fixed 4-class vector. Stage 1 only meaningfully populates
`authentic` and `ai_generated`; `deepfake` and `edited` start at 0.0 until
the corresponding detectors come online in Stage 2. Clients should treat 0.0
as "not assessed" for an unsigned-classifier slot — but the field is always
present so the schema itself never changes.
• `signals` is a list, currently with a single entry for the CLIP detector.
More detectors (frequency, face-swap) append to this list in later stages.
• `provenance` is always present. When C2PA is disabled or unavailable,
`c2pa_present` is False and `c2pa_valid` is None.
"""
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, Field
# The 4-class taxonomy. New classes must NOT be added without bumping API to v2.
Verdict = Literal["authentic", "ai_generated", "deepfake", "edited", "uncertain"]
class Probabilities(BaseModel):
"""Probability mass over the 4 mutually-exclusive classes.
Sums to 1.0 (allowing for floating-point rounding within ±1e-3).
"""
authentic: float = Field(ge=0.0, le=1.0)
ai_generated: float = Field(ge=0.0, le=1.0)
deepfake: float = Field(ge=0.0, le=1.0)
edited: float = Field(ge=0.0, le=1.0)
class DetectorSignal(BaseModel):
"""Per-detector contribution, surfaced for transparency / debugging.
`score` is the detector's own internal "fakeness" estimate in [0, 1];
its meaning depends on the detector. Aggregation into `probabilities`
happens in the ensemble layer.
"""
name: str
score: float = Field(ge=0.0, le=1.0)
notes: str | None = None
class Provenance(BaseModel):
"""C2PA / Content Credentials check.
A trustworthy C2PA manifest from a known camera or generator can short-
circuit the model — those signals are currently advisory only.
"""
c2pa_present: bool
c2pa_valid: bool | None = None # None → not checked / unverifiable
issuer: str | None = None # e.g. "Sony", "OpenAI", "Adobe"
claim_generator: str | None = None # raw claim_generator string from the manifest
class ScanResponse(BaseModel):
"""The /v1/scan/image response. This is the public contract."""
verdict: Verdict
confidence: float = Field(ge=0.0, le=1.0)
probabilities: Probabilities
signals: list[DetectorSignal]
provenance: Provenance
model_version: str
scan_id: str
latency_ms: float
class ErrorResponse(BaseModel):
"""Uniform error shape for non-2xx responses."""
error: str
detail: str | None = None