import gradio as gr import pickle import re from scipy.sparse import hstack # ── Load models ────────────────────────────────────────────────────────────── tfidf = pickle.load(open("tfidf.pkl", "rb")) count_vec = pickle.load(open("count.pkl", "rb")) xgb = pickle.load(open("xgb.pkl", "rb")) # ── Core logic ──────────────────────────────────────────────────────────────── def clean(text: str) -> str: text = text.lower() text = re.sub(r"[^a-zA-Z]", " ", text) return text def predict(news_text: str): if not news_text or not news_text.strip(): return ( "⚠️ Please enter some news text to analyse.", "—", "—", ) cleaned = clean(news_text) t1 = tfidf.transform([cleaned]) t2 = count_vec.transform([cleaned]) features = hstack([t1, t2]) proba = xgb.predict_proba(features)[0] fake_prob = proba[1] real_prob = proba[0] confidence = max(fake_prob, real_prob) * 100 if fake_prob > 0.5: verdict = "❌ FAKE NEWS DETECTED" summary = ( f"Our model is **{confidence:.1f}%** confident this article " f"contains misinformation or fabricated content." ) conf_label = f"{confidence:.1f}% Fake Confidence" else: verdict = "✅ REAL NEWS VERIFIED" summary = ( f"Our model is **{confidence:.1f}%** confident this article " f"is credible and factually grounded." ) conf_label = f"{confidence:.1f}% Real Confidence" return verdict, summary, conf_label # ── Custom CSS — dark purple / Cisco-inspired ───────────────────────────────── CSS = """ /* ── Google Fonts ── */ @import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Source+Sans+3:wght@300;400;600&display=swap'); /* ── Palette ── --cisco-navy : #0d1117 --cisco-deep : #12102b --cisco-purple : #1e1a4a --cisco-mid : #2d2870 --cisco-accent : #49c5f1 (Cisco cyan) --cisco-violet : #7b5ea7 --cisco-text : #cdd6f4 */ body, .gradio-container { background: #0d1117 !important; font-family: 'Source Sans 3', sans-serif !important; color: #cdd6f4 !important; } /* ── Animated gradient background ── */ .gradio-container::before { content: ""; position: fixed; inset: 0; z-index: -1; background: radial-gradient(ellipse 80% 60% at 20% 10%, rgba(73,197,241,.08) 0%, transparent 60%), radial-gradient(ellipse 60% 50% at 80% 80%, rgba(123,94,167,.12) 0%, transparent 55%), radial-gradient(ellipse 100% 80% at 50% 50%, rgba(30,26,74,1) 0%, #0d1117 100%); animation: bgPulse 10s ease-in-out infinite alternate; } @keyframes bgPulse { from { opacity: .85; } to { opacity: 1; } } /* ── Header / hero ── */ .hero-header { text-align: center; padding: 2.4rem 1rem 1rem; border-bottom: 1px solid rgba(73,197,241,.18); margin-bottom: 1.8rem; } .hero-logo { display: inline-flex; align-items: center; gap: .6rem; font-family: 'Rajdhani', sans-serif; font-size: 1rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: #49c5f1; margin-bottom: .7rem; } .hero-logo svg { flex-shrink: 0; } .hero-title { font-family: 'Rajdhani', sans-serif; font-size: clamp(2rem, 5vw, 3.2rem); font-weight: 700; letter-spacing: .04em; color: #ffffff; line-height: 1.1; margin: 0 0 .45rem; } .hero-title span { color: #49c5f1; } .hero-sub { font-size: .95rem; color: #8b9ab8; max-width: 560px; margin: 0 auto; line-height: 1.55; } /* ── Panel cards ── */ .panel-card { background: rgba(30,26,74,.55) !important; border: 1px solid rgba(73,197,241,.14) !important; border-radius: 10px !important; backdrop-filter: blur(12px); padding: 1.4rem !important; transition: border-color .3s; } .panel-card:hover { border-color: rgba(73,197,241,.35) !important; } /* ── Textarea ── */ textarea { background: rgba(13,17,23,.75) !important; border: 1px solid rgba(73,197,241,.22) !important; border-radius: 8px !important; color: #cdd6f4 !important; font-family: 'Source Sans 3', sans-serif !important; font-size: .95rem !important; resize: vertical !important; transition: border-color .25s, box-shadow .25s !important; } textarea:focus { border-color: #49c5f1 !important; box-shadow: 0 0 0 3px rgba(73,197,241,.12) !important; outline: none !important; } textarea::placeholder { color: #4a5568 !important; } /* ── Labels ── */ label span, .block > label { font-family: 'Rajdhani', sans-serif !important; font-size: .78rem !important; font-weight: 600 !important; letter-spacing: .12em !important; text-transform: uppercase !important; color: #49c5f1 !important; } /* ── Analyse button ── */ button#analyse-btn, .gr-button-primary { font-family: 'Rajdhani', sans-serif !important; font-size: 1.05rem !important; font-weight: 700 !important; letter-spacing: .12em !important; text-transform: uppercase !important; background: linear-gradient(135deg, #2d2870 0%, #1e1a4a 100%) !important; border: 1px solid #49c5f1 !important; color: #49c5f1 !important; border-radius: 8px !important; padding: .75rem 2rem !important; cursor: pointer !important; transition: background .25s, box-shadow .25s, transform .15s !important; width: 100% !important; } button#analyse-btn:hover, .gr-button-primary:hover { background: linear-gradient(135deg, #3d37a0 0%, #2d2870 100%) !important; box-shadow: 0 0 22px rgba(73,197,241,.35) !important; transform: translateY(-1px) !important; } button#analyse-btn:active, .gr-button-primary:active { transform: translateY(0) !important; } /* ── Clear / secondary button ── */ .gr-button-secondary { font-family: 'Rajdhani', sans-serif !important; font-size: .88rem !important; font-weight: 600 !important; letter-spacing: .1em !important; text-transform: uppercase !important; background: transparent !important; border: 1px solid rgba(73,197,241,.3) !important; color: #8b9ab8 !important; border-radius: 8px !important; transition: border-color .2s, color .2s !important; } .gr-button-secondary:hover { border-color: #49c5f1 !important; color: #49c5f1 !important; } /* ── Verdict output ── */ #verdict-box textarea, #verdict-box input { font-family: 'Rajdhani', sans-serif !important; font-size: 1.35rem !important; font-weight: 700 !important; text-align: center !important; letter-spacing: .05em !important; border-radius: 8px !important; border: 1px solid rgba(73,197,241,.22) !important; background: rgba(13,17,23,.6) !important; color: #ffffff !important; padding: 1rem !important; } /* ── Summary / confidence outputs ── */ #summary-box textarea, #conf-box textarea, #summary-box input, #conf-box input { background: rgba(13,17,23,.5) !important; border: 1px solid rgba(73,197,241,.14) !important; border-radius: 8px !important; color: #cdd6f4 !important; font-size: .92rem !important; text-align: center !important; } /* ── Horizontal divider ── */ hr { border: none !important; border-top: 1px solid rgba(73,197,241,.15) !important; margin: 1.2rem 0 !important; } /* ── Footer ── */ .footer-bar { text-align: center; padding: 1.2rem 0 .8rem; font-size: .78rem; color: #4a5568; border-top: 1px solid rgba(73,197,241,.1); margin-top: 2rem; letter-spacing: .06em; } .footer-bar a { color: #49c5f1; text-decoration: none; } /* ── Scrollbar ── */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #0d1117; } ::-webkit-scrollbar-thumb { background: #2d2870; border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: #49c5f1; } """ # ── Hero HTML block ───────────────────────────────────────────────────────── HERO_HTML = """
Paste any news article or headline below. Our machine-learning pipeline — TF-IDF · CountVectorizer · XGBoost — will assess its credibility in milliseconds.