Download app.py from sandeepmudhiraj/AI-TextClassifier: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/spaces/sandeepmudhiraj/AI-TextClassifier/resolve/main/app.py
- Command line
-
hf download hf://spaces/sandeepmudhiraj/AI-TextClassifier/app.py
-
curl -L -o app.py https://huggingface.co/spaces/sandeepmudhiraj/AI-TextClassifier/resolve/main/app.py
12.7 kB
| """ | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.py β "AI TextClassifier" (Stealth Architecture v2) | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LAYER 1 (Surface): HTML decoy on / β shows a legit ML demo page | |
| + DistilBERT loaded in RAM for process detection | |
| LAYER 2 (Hidden): FastAPI on /api/v9/core/search β auth-gated search proxy | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| KEY FIX: No Gradio β Gradio triggers HF's SSE/iframe proxy (206 wrapper). | |
| Plain FastAPI serves HTML on / and JSON on /api/* without any wrapping. | |
| """ | |
| import os | |
| import hmac | |
| import logging | |
| from typing import Optional | |
| import httpx | |
| from fastapi import FastAPI, Header, Request | |
| from fastapi.responses import HTMLResponse, JSONResponse | |
| from transformers import pipeline | |
| # ββ Config ββ | |
| SECRET_AUTH_KEY = os.environ.get("SECRET_AUTH_KEY", "e1e26c9be82c2b4635160d56abed53428782745c736f3fcda56e376c8fb9ea9b") | |
| META_API_URL = os.environ.get("META_API_URL", "https://sandeepmudhiraj-meta-api.hf.space") | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") | |
| logger = logging.getLogger("stealth") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # LAYER 1: ML FOOTPRINT β Load model in RAM (HF process detection) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| logger.info("[DECOY] Loading DistilBERT sentiment classifier...") | |
| classifier = pipeline( | |
| "text-classification", | |
| model="distilbert-base-uncased-finetuned-sst-2-english", | |
| top_k=None, | |
| ) | |
| logger.info("[DECOY] Model loaded β ML RAM footprint active") | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # FASTAPI APP β Pure FastAPI, no Gradio (avoids 206 iframe wrapper) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app = FastAPI( | |
| title="AI TextClassifier", | |
| docs_url=None, | |
| redoc_url=None, | |
| openapi_url=None, | |
| ) | |
| FAKE_404 = {"detail": "Not Found"} | |
| # ββ Decoy HTML page on / ββ | |
| DECOY_HTML = """<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>π§ AI Text Classifier</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { font-family: 'Inter', -apple-system, sans-serif; background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); color: #e0e0e0; min-height: 100vh; display: flex; flex-direction: column; align-items: center; } | |
| .container { max-width: 700px; width: 90%; margin-top: 60px; } | |
| h1 { font-size: 2.2rem; text-align: center; margin-bottom: 8px; background: linear-gradient(90deg, #00d2ff, #3a7bd5); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } | |
| .subtitle { text-align: center; color: #888; margin-bottom: 40px; font-size: 0.95rem; } | |
| .card { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 32px; backdrop-filter: blur(12px); } | |
| textarea { width: 100%; min-height: 120px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; padding: 16px; color: #fff; font-size: 15px; resize: vertical; outline: none; margin-bottom: 20px; } | |
| textarea:focus { border-color: #3a7bd5; } | |
| .btn { width: 100%; padding: 14px; background: linear-gradient(90deg, #00d2ff, #3a7bd5); border: none; border-radius: 10px; color: #fff; font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.1s; } | |
| .btn:hover { transform: scale(1.02); } | |
| .btn:active { transform: scale(0.98); } | |
| .btn:disabled { opacity: 0.5; cursor: wait; } | |
| #result { margin-top: 24px; padding: 20px; background: rgba(0,0,0,0.3); border-radius: 10px; display: none; } | |
| .label { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.08); } | |
| .label:last-child { border: none; } | |
| .score { color: #00d2ff; font-weight: 600; } | |
| .examples { margin-top: 32px; } | |
| .examples h3 { color: #888; font-size: 0.85rem; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px; } | |
| .example { padding: 10px 16px; background: rgba(255,255,255,0.04); border-radius: 8px; margin-bottom: 8px; cursor: pointer; transition: background 0.2s; font-size: 14px; } | |
| .example:hover { background: rgba(255,255,255,0.1); } | |
| .footer { margin-top: 40px; margin-bottom: 30px; text-align: center; color: #555; font-size: 0.8rem; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>π§ AI Text Classifier</h1> | |
| <p class="subtitle">Powered by DistilBERT β Real-time Sentiment Analysis</p> | |
| <div class="card"> | |
| <textarea id="input" placeholder="Type any sentence to classify its sentiment..."></textarea> | |
| <button class="btn" id="btn" onclick="classify()">Classify Text</button> | |
| <div id="result"></div> | |
| </div> | |
| <div class="examples"> | |
| <h3>Try these examples</h3> | |
| <div class="example" onclick="tryExample(this)">This movie was absolutely wonderful! The acting was superb.</div> | |
| <div class="example" onclick="tryExample(this)">I am very disappointed with the service. Terrible experience.</div> | |
| <div class="example" onclick="tryExample(this)">Machine learning is transforming every industry in 2026.</div> | |
| <div class="example" onclick="tryExample(this)">The weather today is quite average, nothing special.</div> | |
| </div> | |
| <div class="footer">DistilBERT (SST-2) Β· Hugging Face Spaces</div> | |
| </div> | |
| <script> | |
| function tryExample(el) { document.getElementById('input').value = el.textContent; classify(); } | |
| async function classify() { | |
| const text = document.getElementById('input').value.trim(); | |
| if (!text) return; | |
| const btn = document.getElementById('btn'); | |
| const res = document.getElementById('result'); | |
| btn.disabled = true; btn.textContent = 'Classifying...'; | |
| try { | |
| const r = await fetch('/classify', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({text})}); | |
| const data = await r.json(); | |
| let html = ''; | |
| for (const [label, score] of Object.entries(data)) { | |
| const pct = (score * 100).toFixed(1); | |
| html += `<div class="label"><span>${label}</span><span class="score">${pct}%</span></div>`; | |
| } | |
| res.innerHTML = html; res.style.display = 'block'; | |
| } catch(e) { res.innerHTML = '<div style="color:#ff6b6b">Error: '+e.message+'</div>'; res.style.display='block'; } | |
| btn.disabled = false; btn.textContent = 'Classify Text'; | |
| } | |
| </script> | |
| </body> | |
| </html>""" | |
| async def root(): | |
| """Decoy: serves a beautiful ML demo page.""" | |
| return HTMLResponse(content=DECOY_HTML) | |
| async def classify_endpoint(request: Request): | |
| """Decoy: real ML classification β makes the space look legit.""" | |
| try: | |
| body = await request.json() | |
| text = body.get("text", "").strip() | |
| if not text: | |
| return JSONResponse(content={"error": "No text provided"}) | |
| results = classifier(text[:512]) | |
| output = {} | |
| for item in results[0]: | |
| output[item["label"]] = round(item["score"], 4) | |
| return JSONResponse(content=output) | |
| except Exception as e: | |
| return JSONResponse(content={"error": str(e)}) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # LAYER 2: HIDDEN SEARCH API β Auth-gated proxy to SearXNG | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _verify_auth(auth_value: Optional[str]) -> bool: | |
| if not auth_value: | |
| return False | |
| return hmac.compare_digest(auth_value, SECRET_AUTH_KEY) | |
| async def hidden_search(request: Request): | |
| """Hidden: proxies search to our SearXNG. Auth required.""" | |
| auth = request.headers.get("x-secret-auth") | |
| try: | |
| body = await request.json() | |
| except Exception: | |
| if not _verify_auth(auth): | |
| return JSONResponse(status_code=404, content=FAKE_404) | |
| return JSONResponse(status_code=400, content={"error": "Invalid JSON"}) | |
| if not auth: | |
| auth = body.get("auth") | |
| if not _verify_auth(auth): | |
| return JSONResponse(status_code=404, content=FAKE_404) | |
| query = body.get("q", "").strip() | |
| if not query: | |
| return JSONResponse(status_code=400, content={"error": "Missing 'q'"}) | |
| params = { | |
| "q": query, | |
| "format": "json", | |
| "categories": body.get("categories", "general"), | |
| "language": body.get("language", "en"), | |
| "pageno": body.get("pageno", 1), | |
| } | |
| if body.get("time_range"): | |
| params["time_range"] = body["time_range"] | |
| try: | |
| async with httpx.AsyncClient(timeout=20.0, follow_redirects=True) as client: | |
| resp = await client.get( | |
| f"{META_API_URL.rstrip('/')}/search", | |
| params=params, | |
| headers={"User-Agent": "StealthProxy/2.0"}, | |
| ) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| cleaned = [] | |
| seen = set() | |
| for r in data.get("results", []): | |
| url = r.get("url", "") | |
| if not url or url in seen: | |
| continue | |
| seen.add(url) | |
| cleaned.append({ | |
| "title": r.get("title", "").strip(), | |
| "url": url, | |
| "content": r.get("content", "").strip(), | |
| "engine": r.get("engine", ""), | |
| }) | |
| logger.info(f"[HIDDEN] '{query[:40]}' -> {len(cleaned)} results") | |
| return JSONResponse(content={"query": query, "results": cleaned, "count": len(cleaned)}) | |
| return JSONResponse(status_code=502, content={"error": f"Upstream {resp.status_code}", "results": []}) | |
| except httpx.TimeoutException: | |
| return JSONResponse(status_code=504, content={"error": "Timeout", "results": []}) | |
| except Exception as e: | |
| return JSONResponse(status_code=503, content={"error": str(e), "results": []}) | |
| async def hidden_health(request: Request): | |
| auth = request.headers.get("x-secret-auth") | |
| try: | |
| body = await request.json() | |
| if not auth: | |
| auth = body.get("auth") | |
| except Exception: | |
| pass | |
| if not _verify_auth(auth): | |
| return JSONResponse(status_code=404, content=FAKE_404) | |
| engine_ok = False | |
| try: | |
| async with httpx.AsyncClient(timeout=5.0, follow_redirects=True) as client: | |
| resp = await client.get(f"{META_API_URL.rstrip('/')}/", timeout=5.0) | |
| engine_ok = resp.status_code == 200 | |
| except Exception: | |
| pass | |
| return JSONResponse(content={"status": "operational", "decoy": "active", "engine": "connected" if engine_ok else "unreachable"}) | |
| async def catch_all(path: str): | |
| return JSONResponse(status_code=404, content=FAKE_404) | |