Metadata-Version: 2.4
Name: sentikit
Version: 0.1.0
Summary: A modular sentiment analysis library that handles negation scope, contrastive discourse, idioms, aspect-based sentiment, and sarcasm — goes beyond lexicon tools like VADER/TextBlob.
Author-email: Shefin <shefinedu@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/shefinedu-arch/sentikit
Keywords: nlp,sentiment-analysis,sarcasm-detection,aspect-based-sentiment,negation
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: parse
Requires-Dist: spacy>=3.7; extra == "parse"
Provides-Extra: transformers
Requires-Dist: transformers>=4.40; extra == "transformers"
Requires-Dist: torch>=2.0; extra == "transformers"
Requires-Dist: sentencepiece>=0.2.0; extra == "transformers"
Requires-Dist: protobuf>=4.0; extra == "transformers"
Provides-Extra: all
Requires-Dist: spacy>=3.7; extra == "all"
Requires-Dist: transformers>=4.40; extra == "all"
Requires-Dist: torch>=2.0; extra == "all"
Requires-Dist: sentencepiece>=0.2.0; extra == "all"
Requires-Dist: protobuf>=4.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# sentikit

A sentiment analysis library that goes beyond lexicon tools like VADER and
TextBlob by actually fixing their known failure modes instead of just adding
more words to a dictionary.

## Why not just use VADER?

| Problem | VADER / TextBlob | sentikit |
|---|---|---|
| Negation scope | Fixed 3-4 token window | Real dependency-parse scope (spaCy), or clause-bounded fallback |
| Contrastive discourse ("but", "however") | Averages both clauses | Detects the marker, upweights the post-marker clause |
| Idioms ("break a leg", "a nightmare") | Scores each word literally | Phrase-level lookup, scored as a unit, excluded from word-level pass |
| Aspect-based sentiment | Not supported | Optional transformer stage: per-aspect sentiment, not one blended score |
| Sarcasm | Not supported | Optional transformer gate that dampens/inverts the score by confidence, not a hard flip |
| Implicit / contextual sentiment | Word-lookup only | Optional transformer backbone blended with the rule-based score |

**Honest scope note:** sarcasm and fully implicit sentiment (no sentiment
words at all) are open research problems. sentikit ships best-effort modules
for these with documented confidence handling — it does not claim to solve
them outright.

## Install

```bash
pip install sentikit                 # lite mode only, zero heavy deps
pip install sentikit[parse]          # + spaCy for accurate negation scope
pip install sentikit[transformers]   # + transformer sentiment/sarcasm/ABSA
pip install sentikit[all]            # everything

python -m spacy download en_core_web_sm   # needed if you installed [parse]
```

Lite mode works completely offline with zero downloads. Full mode downloads
models from HuggingFace on first use of that feature (not at import time).

## Usage

### Lite mode (default) — fast, offline, no downloads

```python
from sentikit import Analyzer

sa = Analyzer()  # mode="lite" by default

r = sa.analyze("I do not think this movie was particularly good")
print(r.compound, r.label)          # -0.356 negative
print(r.negation_applied)           # True — correctly scoped "not ... good"
                                     # despite them being far apart

r = sa.analyze("The food was great, but the service was awful")
print(r.label)                      # negative — post-"but" clause dominates

r = sa.analyze("This laptop is a nightmare to work with")
print(r.idioms_matched)             # ['a nightmare']
```

### Full mode — adds transformer sentiment, sarcasm gate, ABSA

```python
sa = Analyzer(mode="full")

result = sa.analyze(
    "The battery life is terrible but the camera is excellent",
    aspects=True,
)
print(result.label)                 # blended rule + transformer score
for a in result.aspects:
    print(a["aspect"], a["sentiment"], a["confidence"])
    # battery life  negative  0.91
    # the camera    positive  0.94

result = sa.analyze("Oh great, another Monday", sarcasm=True)
print(result.sarcasm_probability, result.sarcasm_adjusted, result.label)
```

## Architecture

```
text
 ├─ idiom phrase matching (masks matched spans before word-level scoring)
 ├─ tokenize
 ├─ negation scope (spaCy dependency parse, or clause-bounded regex fallback)
 ├─ contrastive discourse split ("but"/"however"/...) + asymmetric reweighting
 ├─ lexicon lookup + intensifier scaling
 └─ [full mode only]
     ├─ transformer sentiment backbone (blended with rule-based score)
     ├─ sarcasm gate (confidence-weighted dampen/invert, not a hard switch)
     └─ aspect extraction (noun chunks) + per-aspect transformer classification
```

Each module (`negation.py`, `contrast.py`, `idioms.py`, `sarcasm.py`,
`aspect.py`) is independently testable and swappable — this is intentionally
a pipeline of small fit-for-purpose components, not one model trying to do
everything.

## What's still unsolved / roadmap

- **Conditionals** ("it would be amazing if it worked") — not yet detected;
  currently scores the embedded positive word at face value.
- **Rhetorical questions** — not yet detected as a distinct category.
- **Quoted/reported speech attribution** (whose opinion is it?) — not handled.
- **Word sense disambiguation** ("sick" as slang vs. illness) — lexicon uses
  a single fixed value per word; a WSD stage would help here.
- **Idiom coverage** — the bundled idiom lexicon (`data/idioms.json`) is a
  small starter set (~30 entries); swap in a larger corpus (e.g. MAGPIE) for
  production use.

## Development

```bash
pip install -e .[dev,all]
pytest tests/
```
