Spaces:
Sleeping
Sleeping
| 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 = """ | |
| <div class="hero-header"> | |
| <div class="hero-logo"> | |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M12 2L2 7l10 5 10-5-10-5z" fill="#49c5f1"/> | |
| <path d="M2 17l10 5 10-5" stroke="#49c5f1" stroke-width="1.8" stroke-linecap="round"/> | |
| <path d="M2 12l10 5 10-5" stroke="#7b5ea7" stroke-width="1.8" stroke-linecap="round"/> | |
| </svg> | |
| VeritasAI | Powered by XGBoost + NLP | |
| </div> | |
| <h1 class="hero-title">Fake News <span>Detection</span> Engine</h1> | |
| <p class="hero-sub"> | |
| Paste any news article or headline below. Our machine-learning pipeline β | |
| TF-IDF Β· CountVectorizer Β· XGBoost β will assess its credibility in milliseconds. | |
| </p> | |
| </div> | |
| """ | |
| FOOTER_HTML = """ | |
| <div class="footer-bar"> | |
| VeritasAI Β· Built with Gradio & deployed on | |
| <a href="https://huggingface.co/spaces" target="_blank">π€ HuggingFace Spaces</a> | |
| Β· Model: XGBoost + TF-IDF/CountVec | |
| </div> | |
| """ | |
| # ββ Build Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Blocks(css=CSS, title="VeritasAI β Fake News Detector") as demo: | |
| gr.HTML(HERO_HTML) | |
| with gr.Row(): | |
| # ββ Left column: input ββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Column(scale=6, elem_classes="panel-card"): | |
| news_input = gr.Textbox( | |
| label="News Article / Headline", | |
| placeholder=( | |
| "Paste your news article, headline, or any text you want to " | |
| "verifyβ¦\n\nExample: 'Scientists discover that drinking coffee " | |
| "three times a day cures cancer, says new study.'" | |
| ), | |
| lines=12, | |
| max_lines=20, | |
| ) | |
| with gr.Row(): | |
| clear_btn = gr.ClearButton( | |
| components=[news_input], | |
| value="Clear", | |
| variant="secondary", | |
| scale=1, | |
| ) | |
| analyse_btn = gr.Button( | |
| "π Analyse", | |
| variant="primary", | |
| scale=3, | |
| elem_id="analyse-btn", | |
| ) | |
| # ββ Right column: results βββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Column(scale=4, elem_classes="panel-card"): | |
| gr.Markdown("### π Analysis Results") | |
| verdict_out = gr.Textbox( | |
| label="Verdict", | |
| interactive=False, | |
| elem_id="verdict-box", | |
| ) | |
| summary_out = gr.Textbox( | |
| label="Summary", | |
| interactive=False, | |
| lines=3, | |
| elem_id="summary-box", | |
| ) | |
| conf_out = gr.Textbox( | |
| label="Confidence Score", | |
| interactive=False, | |
| elem_id="conf-box", | |
| ) | |
| gr.HTML("<hr/>") | |
| gr.Markdown( | |
| """ | |
| **How it works** | |
| 1. Text is lowercased & cleaned | |
| 2. Vectorised with **TF-IDF** + **CountVectorizer** | |
| 3. Features are stacked β fed to **XGBoost** | |
| 4. Probability threshold: **0.50** | |
| """, | |
| elem_classes="how-it-works", | |
| ) | |
| gr.HTML(FOOTER_HTML) | |
| # ββ Wire up events ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| analyse_btn.click( | |
| fn=predict, | |
| inputs=[news_input], | |
| outputs=[verdict_out, summary_out, conf_out], | |
| ) | |
| news_input.submit( | |
| fn=predict, | |
| inputs=[news_input], | |
| outputs=[verdict_out, summary_out, conf_out], | |
| ) | |
| # ββ Entry point βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if __name__ == "__main__": | |
| demo.launch() | |