Bitcheck-image / app /services /report_builder.py
BitCheck Codex
fix: harden ownership, model loading, and provenance handling
f37ebd2
Raw
History Blame Contribute Delete
3.43 kB
from __future__ import annotations
from typing import Any
from app.services.trust_scorer import build_risk_flags, score_trust
LIMITATIONS = [
"BitCheck provides risk signals and does not make absolute claims that an image is fake or real.",
"Missing metadata or missing C2PA provenance is not proof of AI generation.",
"Classifier output is probabilistic and may produce false positives or false negatives.",
"Visible watermark detection depends on OCR quality and available templates.",
"Forensic indicators are lightweight risk signals, not definitive proof of manipulation.",
"Grad-CAM shows model-influential regions, not confirmed manipulated regions.",
"No official SynthID detector/API is integrated.",
]
def build_report(
verification_id: str,
input_info: dict[str, Any],
filename_analysis: dict[str, Any],
metadata: dict[str, Any],
provenance: dict[str, Any],
visible_watermark_ocr: dict[str, Any],
visible_watermark_template: dict[str, Any],
classifier: dict[str, Any],
forensics: dict[str, Any],
explainability: dict[str, Any],
processing_time_ms: float,
user_email: str,
) -> dict[str, Any]:
trust = score_trust(filename_analysis, metadata, provenance, visible_watermark_ocr, visible_watermark_template, classifier, forensics)
flags = build_risk_flags(filename_analysis, metadata, provenance, visible_watermark_ocr, visible_watermark_template, classifier, forensics, explainability)
report = {
"verification_id": verification_id,
"service": "BitCheck",
"file_type": "image",
"status": "completed",
"processing_time_ms": round(processing_time_ms, 2),
"user_email": user_email,
"input": input_info,
"filename_analysis": filename_analysis,
"metadata": metadata,
"provenance": provenance,
"visible_watermark_ocr": visible_watermark_ocr,
"visible_watermark_template": visible_watermark_template,
"synthid": {
"checked": False,
"status": "not_available",
"reason": "No official SynthID detector/API integrated.",
},
"classifier": classifier,
"forensics": forensics,
"explainability": explainability,
"trust": trust,
"risk_flags": flags,
"recommended_actions": _recommended_actions(trust, flags),
"limitations": LIMITATIONS,
"warnings": _warnings(classifier, explainability, provenance),
}
return report
def _recommended_actions(trust: dict[str, Any], flags: list[str]) -> list[str]:
if trust.get("decision") == "approve":
return ["Proceed if the surrounding business context is acceptable."]
actions = ["Send the image for manual review before relying on it."]
if any("watermark" in flag.lower() for flag in flags):
actions.append("Inspect visible branding or watermark evidence in the image.")
if any("metadata" in flag.lower() for flag in flags):
actions.append("Review extracted metadata and source context.")
return actions
def _warnings(classifier: dict[str, Any], explainability: dict[str, Any], provenance: dict[str, Any]) -> list[str]:
warnings: list[str] = []
for section in (classifier, explainability, provenance):
warning = section.get("warning")
if warning:
warnings.append(str(warning))
return warnings