face-intel / confidence /explainer.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
2.64 kB
"""
Explainer — turns a ConfidenceScore or UnifiedFaceReport into a
multi-line human-readable string for display in the UI / logs.
"""
from __future__ import annotations
from models.reports import (
ConfidenceScore,
ConflictReport,
UnifiedFaceReport,
)
class Explainer:
"""Renders confidence + report as human-readable text."""
@staticmethod
def explain_score(score: ConfidenceScore) -> str:
lines = [f"Overall confidence: {score.overall:.2%} ({score.method})"]
if score.explanation:
lines.append(f" {score.explanation}")
if score.components:
lines.append(" Components:")
for k, v in score.components.items():
lines.append(f" - {k}: {v:.3f}" if isinstance(v, float) else f" - {k}: {v}")
return "\n".join(lines)
@staticmethod
def explain_conflicts(conflicts: list[ConflictReport]) -> str:
if not conflicts:
return "No conflicts detected."
lines = [f"{len(conflicts)} conflict(s) detected:"]
for c in conflicts:
lines.append(
f" [{c.severity.upper()}] {c.kind}: {c.description} "
f"(providers: {', '.join(c.providers)})"
)
return "\n".join(lines)
@staticmethod
def explain_report(report: UnifiedFaceReport) -> str:
m = report.metadata
lines = [
f"Job {m.job_id}{m.total_elapsed_ms:.0f}ms total",
f" Providers invoked: {', '.join(m.providers_invoked) or 'none'}",
f" Succeeded: {', '.join(m.providers_succeeded) or 'none'}",
f" Failed: {', '.join(m.providers_failed) or 'none'}",
f" Detections: {len(report.detections)}",
f" Matches: {len(report.matches)}",
f" Scraped images: {len(report.scraped_images)}",
f" Reverse matches: {len(report.reverse_matches)}",
f" Image analyses: {len(report.image_analyses)}",
f" Metadata extractions: {len(report.metadata_extractions)}",
f" Forensics: {len(report.forensics)}",
f" Conflicts: {len(report.conflicts)}",
]
if m.limitations:
lines.append(" Limitations:")
for lim in m.limitations:
lines.append(f" - {lim}")
if report.overall_confidence:
lines.append("")
lines.append(Explainer.explain_score(report.overall_confidence))
if report.conflicts:
lines.append("")
lines.append(Explainer.explain_conflicts(report.conflicts))
return "\n".join(lines)