Spaces:
Running
Running
| """ | |
| 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 βββββββββββββββββββββββββββββββββββββββ | |
| 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 | |
| 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 βββββββββββββββββββββββββββββ | |
| def serve_index(): | |
| return send_from_directory(app.static_folder, 'index.html') | |
| 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) | |