import streamlit as st import torch import numpy as np import requests import time import re from PIL import Image from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, pipeline, ) from huggingface_hub import hf_hub_download import tensorflow as tf import plotly.graph_objects as go import plotly.express as px # ───────────────────────────────────────────── # PAGE CONFIG # ───────────────────────────────────────────── st.set_page_config( page_title="DeepTrace AI", page_icon="🔬", layout="wide", initial_sidebar_state="collapsed", ) # ───────────────────────────────────────────── # GLOBAL CSS (dark cyber-forensics aesthetic) # ───────────────────────────────────────────── st.markdown("""
""", unsafe_allow_html=True) # ───────────────────────────────────────────── # MODEL LOADERS # ───────────────────────────────────────────── @st.cache_resource(show_spinner=False) def load_text_model(): model_name = "hamzab/roberta-fake-news-classification" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForSequenceClassification.from_pretrained(model_name) model.eval() return tokenizer, model # @st.cache_resource(show_spinner=False) # def load_image_model(): # import os # import keras # Keras 3 explicit import # filenames_to_try = [ # "image_detector_v2.keras", # "image_detector_v2.h5", # ] # path = None # for fname in filenames_to_try: # try: # path = hf_hub_download( # repo_id="syeda-Rija20/image-detector", # filename=fname # ) # break # except Exception: # continue # if path is None: # raise RuntimeError( # "Could not download image model from HuggingFace Hub. " # "Check that 'syeda-Rija20/image-detector' is public and the file exists." # ) # # Try keras 3 native load first, then tf.keras fallback # try: # model = keras.saving.load_model(path, compile=False) # except Exception: # try: # model = tf.keras.models.load_model(path, compile=False) # except Exception as e: # raise RuntimeError(f"Failed to load image model: {e}") @st.cache_resource(show_spinner=False) def load_image_model(): import keras import json config_path = hf_hub_download( repo_id="syeda-Rija20/image-detector", filename="model_config.json" ) weights_path = hf_hub_download( repo_id="syeda-Rija20/image-detector", filename="image_detector_clean.weights.h5" ) with open(config_path) as f: config = f.read() model = keras.models.model_from_json(config) model.load_weights(weights_path) return model # ───────────────────────────────────────────── # PREDICTION FUNCTIONS # ───────────────────────────────────────────── CLICKBAIT_WORDS = [ "SHOCKING", "BREAKING", "EXPOSED", "YOU WON'T BELIEVE", "UNBELIEVABLE", "MUST SEE", "URGENT", "SECRET", "LEAKED", "BANNED", "CENSORED", "THEY DON'T WANT", "EXCLUSIVE" ] FEAR_WORDS = [ "danger", "crisis", "collapse", "attack", "war", "threat", "catastrophe", "disaster", "chaos", "doom", "apocalypse", "deadly", "terror", "panic", "emergency" ] def predict_news(text, tokenizer, model): inputs = tokenizer( text, return_tensors="pt", truncation=True, padding=True, max_length=512 ) with torch.no_grad(): outputs = model(**inputs) probs = torch.nn.functional.softmax(outputs.logits, dim=1) pred = torch.argmax(probs).item() confidence = torch.max(probs).item() * 100 label = model.config.id2label[pred] return label, confidence, probs[0].tolist() def manipulation_score(text): text_up = text.upper() cb_hits = [w for w in CLICKBAIT_WORDS if w in text_up] fear_hits = [w for w in FEAR_WORDS if w.lower() in text.lower()] exclamations = text.count("!") caps_ratio = sum(1 for c in text if c.isupper()) / max(len(text), 1) score = ( len(cb_hits) * 20 + len(fear_hits) * 10 + min(exclamations * 5, 20) + min(caps_ratio * 100, 20) ) return min(int(score), 100), cb_hits, fear_hits def predict_image(img, model): img_r = img.resize((224, 224)).convert("RGB") arr = np.array(img_r, dtype=np.float32) / 255.0 arr = np.expand_dims(arr, axis=0) pred = model.predict(arr, verbose=0) conf = float(pred[0][0]) * 100 # 0 = AI, 1 = Real (based on training) if conf < 50: return "AI GENERATED", 100 - conf else: return "REAL IMAGE", conf # ───────────────────────────────────────────── # CHART HELPERS # ───────────────────────────────────────────── def gauge_chart(value, title, color): fig = go.Figure(go.Indicator( mode = "gauge+number", value = value, title = {"text": title, "font": {"family": "Space Mono", "size": 11, "color": "#64748b"}}, number= {"suffix": "%", "font": {"family": "Syne", "size": 28, "color": "#e2e8f0"}}, gauge = { "axis" : {"range": [0, 100], "tickcolor": "#1e2d45", "tickfont": {"color": "#64748b", "size": 9}}, "bar" : {"color": color, "thickness": 0.3}, "bgcolor" : "#0b1120", "bordercolor": "#1e2d45", "steps" : [ {"range": [0, 33], "color": "rgba(16,185,129,0.08)"}, {"range": [33, 66], "color": "rgba(245,158,11,0.08)"}, {"range": [66,100], "color": "rgba(239,68,68,0.08)"}, ], "threshold" : {"line": {"color": color, "width": 2}, "value": value}, } )) fig.update_layout( paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", margin=dict(t=30, b=10, l=20, r=20), height=200, ) return fig def bar_chart(labels, values, colors): fig = go.Figure(go.Bar( x=values, y=labels, orientation="h", marker_color=colors, marker_line_width=0, text=[f"{v:.1f}%" for v in values], textfont={"family": "Space Mono", "size": 10, "color": "#e2e8f0"}, textposition="outside", )) fig.update_layout( paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", xaxis=dict(range=[0,120], visible=False), yaxis=dict(tickfont={"family": "Space Mono", "size": 10, "color": "#64748b"}), margin=dict(t=10, b=10, l=10, r=60), height=120, showlegend=False, ) return fig # ───────────────────────────────────────────── # HERO # ───────────────────────────────────────────── st.markdown("""Advanced forensic AI for detecting fake news, AI-generated images, and emotional manipulation in digital media.
The model classified this content as
{'FAKE' if is_fake else 'REAL'}
with {conf:.1f}% confidence.
Manipulation score is
{manip_level} ({manip}%)
— detected {len(cb_hits)}
clickbait keyword(s) and
{len(fear_hits)} fear-based word(s).
{'
⚠️ High emotional manipulation detected — verify from multiple sources.' if manip > 50 else ''}
Forensic analysis indicates this image is
{'likely AI-generated' if is_ai else 'likely authentic'}
with {score:.1f}% confidence.
AI generation risk level:
{risk}.
{'
⚠️ Do not use this image as evidence without further verification.' if is_ai else '
✅ Image appears to be from a real camera source.'}
• This tool is for research and educational purposes
— not a definitive fact-checker.
• Image model performance is limited by training data diversity;
very recent AI generators may not be detected.
• Always cross-reference with trusted news sources before
drawing conclusions.
• The manipulation score is heuristic-based and may produce
false positives on legitimate breaking news.