""" Sentiment analyzer. - mode "light": lexicon-based sederhana (cepat, tanpa dependency berat) - mode "transformer": IndoBERT/RoBERTa (akurat, butuh torch + transformers) """ from typing import List, Dict from app.config import settings # Lexicon ringan Bahasa Indonesia (subset). Bisa diperluas. POSITIVE_WORDS = { "baik", "bagus", "hebat", "sukses", "untung", "naik", "tumbuh", "positif", "menang", "juara", "prestasi", "maju", "berhasil", "setuju", "dukung", "apresiasi", "optimis", "damai", "sehat", "aman", "puas", "senang", "bangga", "harapan", "solusi", "peluang", "inovasi", "manfaat", "efektif", } NEGATIVE_WORDS = { "buruk", "jelek", "gagal", "rugi", "turun", "anjlok", "negatif", "kalah", "krisis", "masalah", "korupsi", "tewas", "meninggal", "bencana", "konflik", "protes", "demo", "tolak", "kecam", "marah", "takut", "khawatir", "sakit", "bahaya", "ancaman", "kritik", "lemah", "lambat", "mahal", "sulit", "rusak", "kecewa", "tertekan", "korban", "darurat", } _pipeline = None def _load_transformer(): global _pipeline if _pipeline is None: from transformers import pipeline _pipeline = pipeline( "text-classification", model=settings.SENTIMENT_MODEL, truncation=True, max_length=512, ) return _pipeline def _normalize_label(label: str) -> str: low = label.lower() if "pos" in low: return "positive" if "neg" in low: return "negative" return "neutral" def analyze_light(text: str) -> Dict: tokens = [t.strip(".,!?;:\"'()[]").lower() for t in text.split()] pos = sum(1 for t in tokens if t in POSITIVE_WORDS) neg = sum(1 for t in tokens if t in NEGATIVE_WORDS) total = pos + neg if total == 0: return {"sentiment": "neutral", "score": 0.0, "confidence": 0.5} score = round((pos - neg) / total, 3) if score > 0.15: sentiment = "positive" elif score < -0.15: sentiment = "negative" else: sentiment = "neutral" confidence = round(min(1.0, total / 10), 3) return {"sentiment": sentiment, "score": score, "confidence": confidence} def analyze_transformer(texts: List[str]) -> List[Dict]: pipe = _load_transformer() outputs = pipe(texts) results = [] for out in outputs: label = _normalize_label(out["label"]) conf = round(float(out["score"]), 3) score = conf if label == "positive" else (-conf if label == "negative" else 0.0) results.append({"sentiment": label, "score": round(score, 3), "confidence": conf}) return results def analyze_batch(items: List) -> List[Dict]: if settings.MODEL_MODE == "transformer": try: texts = [it.text[:2000] for it in items] tf = analyze_transformer(texts) return [{"id": it.id, **r} for it, r in zip(items, tf)] except Exception: # Fallback ke light bila transformer gagal load pass return [{"id": it.id, **analyze_light(it.text)} for it in items]