Stateless FastAPI microservice that classifies a digital-finance support ticket (
case_type,severity,department,agent_summary,human_review_required,confidence) from a single JSON request.
Built for the bKash SUST CSE Carnival 2026 Codex Community Hackathon warmup. Rule-engine based, no GPU, no LLM, < 500 ms typical latency.
queue-storm-classifier/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI app: /health and /sort-ticket
│ ├── classifier.py # Rule-engine pipeline
│ ├── rules_config.py # Keywords, regex, dept map (tune me here)
│ ├── safety.py # Summary sanitizer (redaction)
│ └── schemas.py # Pydantic request/response models
├── tests/
│ ├── test_health.py
│ └── test_classification.py
├── Dockerfile
├── .dockerignore
└── requirements.txt
curl http://localhost:8000/health
# → {"status":"ok"}curl -X POST http://localhost:8000/sort-ticket \
-H "Content-Type: application/json" \
-d '{
"ticket_id": "T-001",
"channel": "app",
"locale": "en",
"message": "I sent 5000 taka to a wrong number this morning, please help me get it back"
}'{
"ticket_id": "T-001",
"case_type": "wrong_transfer",
"severity": "high",
"department": "dispute_resolution",
"agent_summary": "Customer reports sending 5000 BDT to a wrong recipient and requests reversal.",
"human_review_required": true,
"confidence": 0.9
}channel and locale are optional. message is required but may be empty
(empty → case_type: "other", severity low, department customer_support).
python -m venv .venv
. .venv/Scripts/activate # Windows
# source .venv/bin/activate # macOS / Linux
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloaddocker build -t queue-storm-classifier .
docker run -p 8000:8000 queue-storm-classifierOverride port via APP_PORT (default 8000); verbosity via LOG_LEVEL
(default info).
docker run -p 9000:9000 -e APP_PORT=9000 -e LOG_LEVEL=debug queue-storm-classifier| Variable | Default | Purpose |
|---|---|---|
APP_PORT |
8000 |
Uvicorn listen port |
LOG_LEVEL |
info |
debug / info / warning / error |
(ENABLE_LLM_FALLBACK, LLM_API_URL, LLM_API_KEY, HF_HOME,
TRANSFORMERS_OFFLINE are accepted by the PRD contract but unused in the
default rules-only build.)
pytest -qThe suite covers all PRD §14 acceptance criteria, including:
- The 5 public sample payloads (cases 2–6).
- Empty / whitespace-only messages (case 8).
- Missing optional fields (case 9).
- Safety redaction of OTP / PIN / card numbers (case 7).
- Bengali (
bn) andmixedlocale routing. - Amount > 50,000 BDT boost to
critical. - Deterministic / idempotent responses.
- Latency guard (< 5 s per request).
The image is a vanilla python:3.11-slim+uvicorn app and works on any
container host. Tested platforms:
- Render: create a Web Service → connect repo → “Docker” environment →
set port
8000→ deploy. - Railway:
New Project → Deploy from Docker Image(or GitHub repo). - Fly.io:
fly launch --dockerfile, thenfly deploy.
Point the platform's health check at GET /health.
main.py validates the JSON via schemas.py and calls
classifier.classify(message, locale). The classifier, driven by
pre-compiled regexes in rules_config.py, picks the highest-priority
case_type whose pattern matches (Bengali equivalents are merged into the
same regex for bn and mixed), applies severity defaults + boosts
(amount > 50,000 BDT, urgency keywords), routes to a department via a
lookup, fills a template-based summary, runs it through safety.sanitize()
to redact OTP/PIN/card-like numbers, and scores a confidence in [0, 1].
The API then sets human_review_required = (severity in {high, critical}) or (case_type == "phishing_or_social_engineering").
Edit app/rules_config.py:
- Add new case types by appending a tuple
(case_type, pattern, default_severity)to the ordered list in_build_rules(). - Add Bengali keywords to the matching
BN_*list. - Change department routing in
CASE_TYPE_TO_DEPARTMENT. - Change the amount threshold via
CRITICAL_AMOUNT_THRESHOLD_BDT.