Spaces:
Sleeping
Sleeping
| import logging | |
| import io | |
| from typing import Optional, List, Dict, Any | |
| from PIL import Image | |
| from ._config import GEMINI_CLIENT, GENAI_MODEL | |
| from ._utils import safe_parse_gemini_json, sanitize_text | |
| from ._media_analyzer import _get_image_data_from_source | |
| logger = logging.getLogger("fact_checker_gemini") | |
| def _build_evidence_snippet(serpapi_web: dict, image_analysis: dict) -> str: | |
| """Creates a text snippet of all gathered evidence for the Gemini prompt.""" | |
| out = "" | |
| try: | |
| organic = serpapi_web.get("result", {}).get("organic_results", []) or [] | |
| pieces = [f"{r.get('title','')} :: {r.get('snippet','')} :: {r.get('link','')}" for r in organic[:6]] | |
| if pieces: out += "WEB EVIDENCE:\n" + "\n".join(pieces) | |
| rorg = image_analysis.get("serpapi_reverse", {}).get("result", {}).get("organic_results", []) or [] | |
| rpieces = [f"{r.get('title','')} :: {r.get('snippet','')} :: {r.get('link','')}" for r in rorg[:4]] | |
| if rpieces: out += "\nREVERSE IMAGE EVIDENCE:\n" + "\n".join(rpieces) | |
| except Exception: | |
| logger.exception("Building evidence snippet failed") | |
| return out | |
| def gemini_generate_claim_from_image(image_source: str) -> Optional[str]: | |
| """Generates a testable factual claim from an image using Gemini's multimodal capacity.""" | |
| if not GEMINI_CLIENT: return None | |
| try: | |
| img_bytes, _ = _get_image_data_from_source(image_source) | |
| if not img_bytes: return None | |
| img = Image.open(io.BytesIO(img_bytes)) | |
| prompt = ( | |
| "You are a cautious fact-check assistant. Look at the image and, ONLY IF you can identify a plausible short factual claim about the main subject, " | |
| "return a JSON object **ONLY** inside triple backticks, with the exact keys: claim, rationale.\n\n" | |
| "Rules:\n- If you can propose a factual testable claim, set \"claim\" to a short sentence (<= 140 chars) starting with " | |
| "\"Auto-generated (unverified):\" and use cautious phrasing. - If you cannot, set \"claim\": null.\n" | |
| ) | |
| resp = GEMINI_CLIENT.models.generate_content(model=GENAI_MODEL, contents=[prompt, img]) | |
| parsed = safe_parse_gemini_json(resp.text) | |
| if parsed and parsed.get("claim"): | |
| return sanitize_text(parsed["claim"])[:400] | |
| return None | |
| except Exception: | |
| logger.exception("Gemini multimodal claim gen failed") | |
| return None | |
| def gemini_extract_claims_from_text(article_text: str, max_claims: int = 3) -> List[Dict[str, str]]: | |
| """Extracts testable claims from article text using Gemini.""" | |
| if not GEMINI_CLIENT: return [] | |
| article_text = sanitize_text(article_text) | |
| if not article_text: return [] | |
| prompt = ( | |
| f"Extract up to {max_claims} concise, testable factual claims from the article. " | |
| "Return ONLY a single fenced JSON block with key `claims` (list of objects with `claim` and `context`). " | |
| "Article:\n | |