File size: 3,069 Bytes
60e8c0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
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]