sentimeter / app.py
rhmnsae's picture
fix routing
07637b4
"""
SentiMeter β€” IndoBERT Sentiment Analysis Backend
Uses mdhugol/indonesia-bert-sentiment-classification
"""
import os
import sys
import traceback
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
app = Flask(__name__, static_folder='.', static_url_path='')
CORS(app)
# ─── Model Loading ────────────────────────────────────
MODEL_NAME = "mdhugol/indonesia-bert-sentiment-classification"
LABEL_MAP = {
'LABEL_0': 'Positif',
'LABEL_1': 'Netral',
'LABEL_2': 'Negatif',
}
sentiment_pipeline = None
model_error = None
def load_model():
"""Load the IndoBERT sentiment model. Returns True on success."""
global sentiment_pipeline, model_error
try:
print(f"[IndoBERT] Loading model: {MODEL_NAME} ...")
from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME)
sentiment_pipeline = pipeline(
"sentiment-analysis",
model=model,
tokenizer=tokenizer,
truncation=True,
max_length=512,
)
model_error = None
print("[IndoBERT] Model loaded successfully!")
return True
except Exception as e:
model_error = str(e)
sentiment_pipeline = None
print(f"[IndoBERT] ERROR loading model: {e}", file=sys.stderr)
traceback.print_exc()
return False
# ─── API Routes ───────────────────────────────────────
@app.route('/api/health', methods=['GET'])
def health():
"""Check if the model is loaded and ready."""
if sentiment_pipeline is not None:
return jsonify({"status": "ok", "model": MODEL_NAME})
else:
return jsonify({
"status": "error",
"model": MODEL_NAME,
"error": model_error or "Model not loaded"
}), 503
@app.route('/api/sentiment', methods=['POST'])
def analyze_sentiment():
"""
Analyze sentiment for a batch of texts.
Expects JSON: { "texts": ["text1", "text2", ...] }
Returns JSON: [ {"label": "Positif", "score": 0.95}, ... ]
"""
if sentiment_pipeline is None:
return jsonify({
"error": True,
"message": f"Model IndoBERT gagal dimuat: {model_error or 'Unknown error'}. "
"Pastikan koneksi internet aktif dan dependensi sudah terinstal.",
}), 503
data = request.get_json(silent=True)
if not data or 'texts' not in data:
return jsonify({"error": True, "message": "Request harus berisi field 'texts'."}), 400
texts = data['texts']
if not isinstance(texts, list) or len(texts) == 0:
return jsonify({"error": True, "message": "'texts' harus berupa array string yang tidak kosong."}), 400
# Limit batch size to prevent OOM
MAX_BATCH = 64
results = []
try:
for i in range(0, len(texts), MAX_BATCH):
batch = texts[i:i + MAX_BATCH]
# Ensure all texts are non-empty strings
batch = [str(t).strip() or "." for t in batch]
preds = sentiment_pipeline(batch)
for pred in preds:
label = LABEL_MAP.get(pred['label'], pred['label'])
results.append({
"label": label,
"score": round(pred['score'], 4),
})
except Exception as e:
return jsonify({
"error": True,
"message": f"Error saat proses sentimen: {str(e)}"
}), 500
return jsonify(results)
# ─── Static File Serving ─────────────────────────────
@app.route('/')
def serve_index():
return send_from_directory(app.static_folder, 'index.html')
@app.errorhandler(404)
def handle_404(e):
path = request.path.lstrip('/')
if not path:
return send_from_directory(app.static_folder, 'index.html')
# Try serving the file as is (for static assets)
if os.path.isfile(os.path.join(app.static_folder, path)):
return send_from_directory(app.static_folder, path)
# Try adding .html (for clean URLs)
html_path = path + ".html"
if os.path.isfile(os.path.join(app.static_folder, html_path)):
return send_from_directory(app.static_folder, html_path)
# Fallback to index.html
return send_from_directory(app.static_folder, 'index.html')
# ─── Main ────────────────────────────────────────────
if __name__ == '__main__':
load_model()
print("\n" + "=" * 50)
if sentiment_pipeline:
print(" SentiMeter Server β€” IndoBERT Ready")
else:
print(" SentiMeter Server β€” WARNING: Model failed to load!")
print(f" Error: {model_error}")
print(f" Open: http://localhost:7860")
print("=" * 50 + "\n")
app.run(host='0.0.0.0', port=7860, debug=False)