news / _model_interface.py
mdAmin313's picture
Create _model_interface.py
f7909f6 verified
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