will702 commited on
Commit
ff7107e
·
verified ·
1 Parent(s): e717d81

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +14 -0
  2. README.md +18 -5
  3. app.py +72 -0
  4. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN pip install --no-cache-dir torch>=2.6.0 --index-url https://download.pytorch.org/whl/cpu
6
+
7
+ COPY requirements.txt .
8
+ RUN pip install --no-cache-dir -r requirements.txt
9
+
10
+ COPY app.py .
11
+
12
+ EXPOSE 7860
13
+
14
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,23 @@
1
  ---
2
- title: Sentiment Analysis V1
3
- emoji: 🌖
4
- colorFrom: gray
5
- colorTo: red
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: StockPro Sentiment V1
3
+ emoji: 📊
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: docker
7
  pinned: false
8
+ app_port: 7860
9
  ---
10
 
11
+ # StockPro Sentiment V1 (Comparison)
12
+
13
+ Uses `ihsan31415/indo-roBERTa-financial-sentiment` — Indonesian financial RoBERTa, 97.49% accuracy.
14
+
15
+ ## API
16
+
17
+ ### `POST /predict`
18
+
19
+ ```json
20
+ { "texts": ["BBCA cetak laba bersih Rp50 triliun"] }
21
+ ```
22
+
23
+ ### `GET /health`
app.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from contextlib import asynccontextmanager
3
+
4
+ from fastapi import FastAPI, HTTPException, Request
5
+ from pydantic import BaseModel
6
+ from transformers import pipeline
7
+
8
+ MODEL_NAME = "ihsan31415/indo-roBERTa-financial-sentiment"
9
+ API_KEY = os.getenv("API_KEY")
10
+
11
+ # Label mapping — flipped: 0=Positive, 1=Neutral, 2=Negative
12
+ LABEL_MAP = {
13
+ "label_0": "positive",
14
+ "label_1": "neutral",
15
+ "label_2": "negative",
16
+ "positive": "positive",
17
+ "neutral": "neutral",
18
+ "negative": "negative",
19
+ }
20
+
21
+ classifier = None
22
+
23
+
24
+ @asynccontextmanager
25
+ async def lifespan(app: FastAPI):
26
+ global classifier
27
+ print(f"Loading model: {MODEL_NAME}")
28
+ classifier = pipeline("text-classification", model=MODEL_NAME)
29
+ print("Model loaded.")
30
+ yield
31
+
32
+
33
+ app = FastAPI(title="StockPro Sentiment V1", lifespan=lifespan)
34
+
35
+
36
+ class PredictRequest(BaseModel):
37
+ texts: list[str]
38
+
39
+
40
+ @app.post("/predict")
41
+ async def predict(body: PredictRequest, request: Request):
42
+ if API_KEY:
43
+ key = request.headers.get("X-API-Key")
44
+ if key != API_KEY:
45
+ raise HTTPException(status_code=401, detail="Invalid API key")
46
+
47
+ texts = body.texts
48
+ if not texts:
49
+ raise HTTPException(status_code=400, detail="texts must not be empty")
50
+ if len(texts) > 20:
51
+ raise HTTPException(status_code=400, detail="Maximum 20 texts per request")
52
+
53
+ if classifier is None:
54
+ raise HTTPException(status_code=503, detail="Model not loaded yet")
55
+
56
+ predictions = classifier(texts, truncation=True, max_length=512)
57
+
58
+ results = []
59
+ for text, pred in zip(texts, predictions):
60
+ label = LABEL_MAP.get(pred["label"].lower(), "neutral")
61
+ results.append({
62
+ "text": text,
63
+ "sentiment": label,
64
+ "score": round(pred["score"], 4),
65
+ })
66
+
67
+ return {"results": results}
68
+
69
+
70
+ @app.get("/health")
71
+ def health():
72
+ return {"status": "ok", "model_loaded": classifier is not None, "model": MODEL_NAME}
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi==0.115.5
2
+ uvicorn[standard]==0.32.1
3
+ transformers>=4.51.0
4
+ torch>=2.6.0