Spaces:
Sleeping
Sleeping
| import joblib | |
| import html | |
| import re | |
| from fastapi import FastAPI | |
| from pydantic import BaseModel | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from bs4 import BeautifulSoup | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Load the models | |
| model = joblib.load('model.joblib') | |
| tfidf = joblib.load('tfidf.joblib') | |
| def clean_text(text): | |
| if not isinstance(text, str): | |
| return "" | |
| text = html.unescape(text) | |
| text = BeautifulSoup(text, "html.parser").get_text() | |
| text = re.sub(r'http\S+|www\S+|https\S+', '', text, flags=re.MULTILINE) | |
| text = re.sub(r'\S*@\S*\s?', '', text) | |
| text = re.sub(r'[^a-zA-Z\s]', '', text) | |
| text = re.sub(r'\s+', ' ', text).strip() | |
| return text.lower() | |
| class ReviewRequest(BaseModel): | |
| text: str | |
| def predict(request: ReviewRequest): | |
| cleaned = clean_text(request.text) | |
| # Very short edge cases check | |
| if len(cleaned.split()) < 2: | |
| return {"sentiment": "Neutral", "confidence": 0.5, "cleaned": cleaned} | |
| vector = tfidf.transform([cleaned]) | |
| prediction = model.predict(vector)[0] | |
| probabilities = model.predict_proba(vector)[0] | |
| confidence = float(max(probabilities)) | |
| sentiment = "Positive" if prediction == 1 else "Negative" | |
| return {"sentiment": sentiment, "confidence": confidence, "cleaned": cleaned} | |
| from fastapi.responses import HTMLResponse | |
| import markdown2 | |
| def read_root(): | |
| try: | |
| with open("README.md", "r") as f: | |
| content = f.read() | |
| metadata = {} | |
| if content.startswith("---"): | |
| parts = content.split("---", 2) | |
| if len(parts) >= 3: | |
| import yaml | |
| metadata = yaml.safe_load(parts[1]) | |
| content = parts[2] | |
| html_content = markdown2.markdown(content.strip()) | |
| tags = [ | |
| ("Sentiment Analysis", "#3b82f6"), | |
| ("Logistic Regression", "#6366f1"), | |
| ("TF-IDF", "#8b5cf6"), | |
| ("Docker", "#10b981"), | |
| ("FastAPI", "#f59e0b") | |
| ] | |
| tags_html = "".join([f'<span class="badge" style="background: {color}20; color: {color}; border: 1px solid {color}40;">{name}</span>' for name, color in tags]) | |
| return f""" | |
| <html> | |
| <head> | |
| <title>{metadata.get('title', 'Model Card')}</title> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-dark.min.css"> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body {{ | |
| background-color: #0b0f19; | |
| color: #f3f4f6; | |
| font-family: 'Inter', -apple-system, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| display: flex; | |
| justify-content: center; | |
| }} | |
| .container {{ | |
| width: 100%; | |
| max-width: 1000px; | |
| background: #111827; | |
| border: 1px solid #1f2937; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4); | |
| }} | |
| .header_section {{ | |
| padding: 32px; | |
| border-bottom: 1px solid #1f2937; | |
| background: radial-gradient(circle at top right, #1e1b4b 0%, #111827 50%); | |
| }} | |
| .title_row {{ | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin-bottom: 20px; | |
| }} | |
| .emoji_icon {{ font-size: 28px; }} | |
| .main_title {{ | |
| font-size: 26px; | |
| font-weight: 700; | |
| color: #ffffff; | |
| margin: 0; | |
| letter-spacing: -0.025em; | |
| }} | |
| .badge_container {{ | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| }} | |
| .badge {{ | |
| padding: 4px 12px; | |
| border-radius: 6px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| }} | |
| .body_content {{ | |
| padding: 40px; | |
| background: #0b0f19; | |
| }} | |
| .markdown-body {{ | |
| background-color: transparent !important; | |
| font-family: 'Inter', sans-serif !important; | |
| color: #d1d5db !important; | |
| font-size: 16px; | |
| }} | |
| .markdown-body h1, .markdown-body h2 {{ border-bottom: 1px solid #374151; padding-bottom: 8px; color: #fff; }} | |
| .status_footer {{ | |
| margin-top: 40px; | |
| padding: 24px; | |
| background: #111827; | |
| border-top: 1px solid #1f2937; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| }} | |
| .status_indicator {{ | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| }} | |
| .pulse_dot {{ | |
| height: 10px; | |
| width: 10px; | |
| background-color: #10b981; | |
| border-radius: 50%; | |
| animation: pulse 2s infinite; | |
| }} | |
| @keyframes pulse {{ | |
| 0% {{ box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }} | |
| 70% {{ box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }} | |
| 100% {{ box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }} | |
| }} | |
| .endpoint_pill {{ | |
| background: #1f2937; | |
| padding: 6px 14px; | |
| border-radius: 8px; | |
| font-family: monospace; | |
| font-size: 14px; | |
| color: #3b82f6; | |
| border: 1px solid #374151; | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header_section"> | |
| <div class="title_row"> | |
| <span class="emoji_icon">{metadata.get('emoji', '�️')}</span> | |
| <h1 class="main_title">{metadata.get('title', 'Amazon Sentiment API')}</h1> | |
| </div> | |
| <div class="badge_container"> | |
| {tags_html} | |
| <span class="badge" style="background: #374151; color: #9ca3af; border: 1px solid #4b5563;">License: {metadata.get('license', 'MIT')}</span> | |
| </div> | |
| </div> | |
| <div class="body_content"> | |
| <article class="markdown-body"> | |
| {html_content} | |
| </article> | |
| </div> | |
| <div class="status_footer"> | |
| <div class="status_indicator"> | |
| <div class="pulse_dot"></div> | |
| <span style="font-weight:700; color:#fff; font-size:15px;">API ONLINE</span> | |
| </div> | |
| <div class="endpoint_pill">POST /predict</div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| except Exception as e: | |
| return f"<div style='color:red; font-family:sans-serif;'>Failed to render Model Card: {str(e)}</div>" | |