sahilskadam's picture
Upload folder using huggingface_hub
252ae39 verified
Raw
History Blame Contribute Delete
8.59 kB
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
@app.post("/predict")
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
@app.get("/", response_class=HTMLResponse)
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>"