Spaces:
Paused
Paused
| from __future__ import annotations | |
| import os | |
| class ExplainModule: | |
| """Google Gemini text explainer.""" | |
| def __init__(self): | |
| self.model = os.environ.get("GEMINI_EXPLAIN_MODEL", "gemini-3.1-flash-lite-preview") | |
| self.client = None | |
| try: | |
| from google import genai | |
| self.client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY", "")) | |
| except Exception as exc: | |
| print(f"ExplainModule using Gemini fallback: {exc}") | |
| def explain( | |
| self, | |
| fakescore, | |
| s1, | |
| s2, | |
| s3, | |
| weights, | |
| attribution, | |
| segments, | |
| top_generator, | |
| ) -> str: | |
| verdict = "FAKE" if fakescore > 0.5 else "REAL" | |
| confidence = ( | |
| "high" | |
| if abs(fakescore - 0.5) > 0.3 | |
| else "moderate" | |
| if abs(fakescore - 0.5) > 0.15 | |
| else "low" | |
| ) | |
| seg_text = "" | |
| if segments: | |
| seg_text = "Flagged timestamps: " + ", ".join( | |
| [f"{segment['time']}s (score={segment['score']})" for segment in segments[:5]] | |
| ) | |
| attr_text = "" | |
| if attribution: | |
| top3 = sorted(attribution.items(), key=lambda item: -item[1])[:3] | |
| attr_text = "Top generators: " + ", ".join( | |
| [f"{name}: {prob * 100:.1f}%" for name, prob in top3] | |
| ) | |
| prompt = f"""You are a forensic AI analyst. Analyze these deepfake detection results. Be specific about evidence. | |
| Results: | |
| - Verdict: {verdict} (FakeScore: {fakescore:.3f}, confidence: {confidence}) | |
| - Lip-Sync (M1): {s1:.3f} (weight: {weights.get('lip_sync', 'N/A')}) | |
| - Fingerprint (M2): {s2:.3f} (weight: {weights.get('fingerprint', 'N/A')}) | |
| - Graph-GNN (M3): {s3:.3f} (weight: {weights.get('graph_gnn', 'N/A')}) | |
| {seg_text} | |
| {attr_text} | |
| - Most likely generator: {top_generator} | |
| Write 3-5 sentences. Reference specific scores and timestamps.""" | |
| try: | |
| if self.client is None: | |
| raise RuntimeError("Gemini client unavailable") | |
| from google.genai import types | |
| response = self.client.models.generate_content( | |
| model=self.model, | |
| contents=prompt, | |
| config=types.GenerateContentConfig( | |
| system_instruction="You are a forensic deepfake analyst. Be precise.", | |
| max_output_tokens=300, | |
| temperature=0.3, | |
| ), | |
| ) | |
| return (response.text or "").strip() | |
| except Exception: | |
| return self._fallback(verdict, fakescore, s1, s2, s3, top_generator, confidence) | |
| def _fallback(self, verdict, fakescore, s1, s2, s3, top_gen, conf): | |
| if verdict == "FAKE": | |
| return ( | |
| f"Video classified as {verdict} with {conf} confidence " | |
| f"(FakeScore: {fakescore:.3f}). " | |
| f"Lip-sync scored {s1:.2f}, indicating " | |
| f"{'significant' if s1 > 0.7 else 'moderate' if s1 > 0.5 else 'minimal'} " | |
| f"audio-visual inconsistency. " | |
| f"Style fingerprinting scored {s2:.2f}, top attribution: {top_gen}. " | |
| f"Graph analysis scored {s3:.2f}." | |
| ) | |
| return ( | |
| f"Video classified as {verdict} with {conf} confidence " | |
| f"(FakeScore: {fakescore:.3f}). " | |
| f"All modules returned scores below detection threshold." | |
| ) | |