kn29 commited on
Commit
58e298f
·
verified ·
1 Parent(s): d93db71

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -0
app.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Hugging Face Space Application
3
+ Performs summarization (BART) and sentiment analysis on news articles
4
+ """
5
+
6
+ from fastapi import FastAPI, HTTPException, Header
7
+ from pydantic import BaseModel
8
+ from transformers import pipeline
9
+ import os
10
+ from typing import Optional
11
+
12
+ # Initialize FastAPI app
13
+ app = FastAPI(title="News Analysis API")
14
+
15
+ # Load models at startup (cached for performance)
16
+ print("Loading models...")
17
+ summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
18
+ sentiment_analyzer = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")
19
+ print("Models loaded successfully!")
20
+
21
+ # Request/Response models
22
+ class ArticleRequest(BaseModel):
23
+ text: str
24
+ max_summary_length: Optional[int] = 150
25
+ min_summary_length: Optional[int] = 50
26
+
27
+ class ArticleResponse(BaseModel):
28
+ summary: str
29
+ sentiment: str
30
+ sentiment_score: float
31
+ original_length: int
32
+ summary_length: int
33
+
34
+ # Simple API key authentication (set in HF Space secrets)
35
+ EXPECTED_API_KEY = os.environ.get("API_KEY", "your-secret-key-here")
36
+
37
+ def verify_api_key(authorization: str = Header(None)):
38
+ """Verify the API key from Authorization header"""
39
+ if not authorization:
40
+ raise HTTPException(status_code=401, detail="Missing Authorization header")
41
+
42
+ # Expected format: "Bearer <token>"
43
+ try:
44
+ scheme, token = authorization.split()
45
+ if scheme.lower() != "bearer" or token != EXPECTED_API_KEY:
46
+ raise HTTPException(status_code=401, detail="Invalid API key")
47
+ except ValueError:
48
+ raise HTTPException(status_code=401, detail="Invalid Authorization header format")
49
+
50
+ return token
51
+
52
+ @app.get("/")
53
+ def read_root():
54
+ """Health check endpoint"""
55
+ return {
56
+ "status": "healthy",
57
+ "service": "News Analysis API",
58
+ "models": {
59
+ "summarization": "facebook/bart-large-cnn",
60
+ "sentiment": "distilbert-base-uncased-finetuned-sst-2-english"
61
+ }
62
+ }
63
+
64
+ @app.post("/analyze", response_model=ArticleResponse)
65
+ def analyze_article(
66
+ article: ArticleRequest,
67
+ authorization: str = Header(None)
68
+ ):
69
+ """
70
+ Analyze a news article: summarize and determine sentiment
71
+
72
+ Args:
73
+ article: ArticleRequest with text to analyze
74
+ authorization: Bearer token for authentication
75
+
76
+ Returns:
77
+ ArticleResponse with summary and sentiment
78
+ """
79
+ # Verify API key
80
+ verify_api_key(authorization)
81
+
82
+ try:
83
+ # Validate input
84
+ if not article.text or len(article.text.strip()) < 50:
85
+ raise HTTPException(
86
+ status_code=400,
87
+ detail="Article text must be at least 50 characters"
88
+ )
89
+
90
+ text = article.text.strip()
91
+ original_length = len(text)
92
+
93
+ # STEP 1: Summarization using BART
94
+ # Truncate if text is too long (BART has max input length)
95
+ max_input_length = 1024
96
+ truncated_text = text[:max_input_length] if len(text) > max_input_length else text
97
+
98
+ summary_result = summarizer(
99
+ truncated_text,
100
+ max_length=article.max_summary_length,
101
+ min_length=article.min_summary_length,
102
+ do_sample=False
103
+ )
104
+
105
+ summary = summary_result[0]['summary_text']
106
+
107
+ # STEP 2: Sentiment Analysis
108
+ # Use the summary for sentiment (more focused analysis)
109
+ sentiment_result = sentiment_analyzer(summary[:512]) # Sentiment model max length
110
+
111
+ sentiment_label = sentiment_result[0]['label'] # POSITIVE or NEGATIVE
112
+ sentiment_score = sentiment_result[0]['score']
113
+
114
+ # Return structured response
115
+ return ArticleResponse(
116
+ summary=summary,
117
+ sentiment=sentiment_label,
118
+ sentiment_score=round(sentiment_score, 4),
119
+ original_length=original_length,
120
+ summary_length=len(summary)
121
+ )
122
+
123
+ except Exception as e:
124
+ raise HTTPException(
125
+ status_code=500,
126
+ detail=f"Error processing article: {str(e)}"
127
+ )
128
+
129
+ @app.get("/health")
130
+ def health_check():
131
+ """Detailed health check"""
132
+ return {
133
+ "status": "healthy",
134
+ "models_loaded": True,
135
+ "summarizer": "ready",
136
+ "sentiment_analyzer": "ready"
137
+ }
138
+
139
+ if __name__ == "__main__":
140
+ import uvicorn
141
+ uvicorn.run(app, host="0.0.0.0", port=7860)