A Django REST backend for an AI customer-service desk. It combines a retrieval-augmented (RAG) chatbot that answers from your own uploaded documents, a full grievance/ticketing workflow with email notifications, and real-time chat over WebSockets — all behind JWT-authenticated APIs.
Built as a backend internship project (Sept 2025) to explore how an LLM can be grounded in private company knowledge instead of answering from generic model memory.
A plain LLM chatbot hallucinates and can't answer questions about your policies, products, or procedures. AriaAI fixes that by:
- Letting staff upload company documents, which are chunked and embedded into a searchable vector store.
- On each user question, retrieving the most relevant chunks and feeding them to the model as context — so answers are grounded in real documentation.
- Escalating anything the bot can't resolve into a tracked grievance that moves through a human assign → resolve → rate workflow with emails at every step.
| Area | What it does |
|---|---|
| RAG chatbot | Answers grounded in uploaded docs via the Claude API (claude-opus-4-8) over locally-embedded context |
| Document ingestion | Sentence-aware chunking, batched embeddings, vector storage |
| Semantic search | Cosine-similarity search across all processed documents |
| Grievance system | Create → assign → resolve → rate, with HTML email at each stage |
| Real-time chat | WebSocket consumer (Django Channels) with JWT auth |
| Auth & ownership | JWT (SimpleJWT); users only ever see their own sessions/docs/tickets; staff see all grievances |
| Admin | Django admin over every model |
POST /api/documents/upload_and_process/
│
▼
RAGProcessor.process_document()
├─ _chunk_text() sentence-aware ~1000-char chunks, 200-char overlap
├─ _create_embeddings() local deterministic hashing embedding (no API call)
└─ persist DocumentChunk rows (content + embedding stored as JSON)
User ─▶ POST /api/chat/sessions/{id}/send_message/ (or the WebSocket)
│
▼
ChatbotEngine.process_chat_message()
│
├─▶ RAGProcessor.get_relevant_context(query)
│ ├─ embed the query (local hashing embedding)
│ ├─ cosine similarity vs every stored chunk vector
│ └─ pack top matches into a ~2000-token context budget
│
└─▶ Claude (claude-opus-4-8) ◀─ system prompt + retrieved context + user message
│
▼
assistant reply ─▶ saved as a ChatMessage
(with token count + context metadata)
Retrieval is a straightforward in-Python cosine-similarity scan over chunk vectors stored as JSON — simple and dependency-light, ideal for a demo-scale corpus. Embeddings are computed locally (a deterministic hashing embedding), so the whole pipeline runs with only a Claude API key and no second vendor. (See Scaling notes for the production path.)
- Framework: Django 5.2 + Django REST Framework
- Auth: JWT via
djangorestframework-simplejwt - AI/ML: Anthropic Claude API (
claude-opus-4-8) for chat; local hashing embedding for retrieval (no embeddings vendor required) - Real-time: Django Channels (in-memory channel layer; Redis-ready)
- Database: SQLite by default (swappable for PostgreSQL/MySQL)
- Email: SMTP (console backend used automatically when no credentials are set)
ariaai/
├── config/ # Django project (settings, URLs, ASGI/WSGI)
│ ├── settings.py # env-driven config, JWT/CORS/Channels setup
│ ├── urls.py # API root + admin + JWT token routes
│ └── asgi.py # HTTP + WebSocket (ProtocolTypeRouter)
├── chat/ # The single application
│ ├── models.py # UserProfile, ChatSession, ChatMessage,
│ │ # Document, DocumentChunk, Grievance
│ ├── views.py # DRF viewsets + auth + chatbot/health endpoints
│ ├── serializers.py # Request/response schemas
│ ├── rag_utils.py # RAGProcessor + ChatbotEngine (the core logic)
│ ├── email_utils.py # Grievance/chatbot notification emails
│ ├── consumers.py # WebSocket chat consumer
│ ├── middleware.py # JWT authentication for WebSocket connections
│ ├── routing.py # WebSocket URL routing
│ ├── templates/emails/ # HTML email templates
│ └── tests.py # Auth, grievance, RAG, and health-check tests
├── requirements.txt
└── .env.example
- UserProfile — phone/address, one-to-one with Django's
User. - ChatSession / ChatMessage — conversations and their turns (UUID keys; messages carry RAG metadata like tokens used and context length).
- Document / DocumentChunk — uploaded source text and its embedded chunks.
- Grievance — a support ticket with status, priority, assignee, resolution notes, and a 1–5 satisfaction rating.
- Python 3.10+
- An Anthropic API key (required for the chatbot; document search/RAG works without it)
# 1. Clone
git clone https://github.com/jaisinha77777/ariaai.git
cd ariaai
# 2. Virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. Dependencies
pip install -r requirements.txt
# 4. Environment variables
cp .env.example .env # then edit .env (see below)
# 5. Database
python manage.py migrate
# 6. Admin user
python manage.py createsuperuser
# 7. Run
python manage.py runserverVisit http://localhost:8000/ for the API index, or /admin/ for the admin.
# Django core
DJANGO_SECRET_KEY=your-random-secret-key # generate a fresh one for any real deployment
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
# Anthropic (Claude)
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# ANTHROPIC_MODEL=claude-opus-4-8 # optional override
# Email — leave blank to print emails to the console instead of sending
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
DEFAULT_FROM_EMAIL=your-email@gmail.com
SITE_URL=http://localhost:8000No secrets are committed: every credential is read from the environment, and email falls back to the console backend when SMTP isn't configured, so the app runs end-to-end without external services.
The default channel layer is in-memory, so WebSockets work out of the box for local development. Redis (
channels-redis) is only needed to scale the channel layer across multiple processes in production.
All /api/... routes except registration, login, and health require an
Authorization: Bearer <access_token> header.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register/ |
Register a user (optionally with a profile) |
POST |
/api/auth/login/ |
Log in, returns access + refresh tokens |
GET/PUT |
/api/auth/profile/ |
View / update the current user's profile |
POST |
/api/token/ · /api/token/refresh/ |
Obtain / refresh a JWT |
| Method | Endpoint | Description |
|---|---|---|
GET/POST |
/api/chat/sessions/ |
List or create chat sessions |
GET/PUT/DELETE |
/api/chat/sessions/{id}/ |
Manage a session |
POST |
/api/chat/sessions/{id}/send_message/ |
Send a message, get the bot's reply |
GET/POST |
/api/chat/messages/ |
List or create messages |
POST |
/api/chatbot/query/ |
One-shot chatbot query (no session) |
| Method | Endpoint | Description |
|---|---|---|
GET/POST |
/api/documents/ |
List or create documents |
POST |
/api/documents/upload_and_process/ |
Upload text and embed it immediately |
GET |
/api/documents/{id}/chunks/ |
Inspect a document's chunks |
POST |
/api/documents/search/ |
Semantic search across documents |
| Method | Endpoint | Description |
|---|---|---|
GET/POST |
/api/grievances/ |
List or file a grievance |
POST |
/api/grievances/{id}/assign/ |
Assign to staff (staff only) |
POST |
/api/grievances/{id}/resolve/ |
Resolve with notes (staff only) |
POST |
/api/grievances/{id}/rate/ |
Rate the resolution 1–5 (owner only) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health/ |
Health check (DB + Anthropic config status) |
WS |
ws://localhost:8000/ws/chat/{session_id}/?token={JWT} |
Real-time chat stream |
The WebSocket authenticates with the same JWT as the REST API (passed as the
token query param). Send {"message": "..."} frames; the server replies with
user_message and assistant_message events and persists both to the session.
Register
curl -X POST http://localhost:8000/api/auth/register/ \
-H "Content-Type: application/json" \
-d '{"username":"testuser","email":"test@example.com",
"password":"password123","confirm_password":"password123"}'Upload a document for RAG
curl -X POST http://localhost:8000/api/documents/upload_and_process/ \
-H "Authorization: Bearer YOUR_JWT" -H "Content-Type: application/json" \
-d '{"title":"Remote Work Policy","content":"Employees may work remotely..."}'Ask a grounded question
# Create a session
curl -X POST http://localhost:8000/api/chat/sessions/ \
-H "Authorization: Bearer YOUR_JWT" -H "Content-Type: application/json" \
-d '{"title":"Support Chat"}'
# Send a message (uses RAG over your uploaded docs)
curl -X POST http://localhost:8000/api/chat/sessions/<SESSION_ID>/send_message/ \
-H "Authorization: Bearer YOUR_JWT" -H "Content-Type: application/json" \
-d '{"message":"What is the company policy on remote work?"}'File a grievance
curl -X POST http://localhost:8000/api/grievances/ \
-H "Authorization: Bearer YOUR_JWT" -H "Content-Type: application/json" \
-d '{"subject":"Issue with service","description":"Details...",
"category":"service","priority":"medium","email":"user@example.com"}'Tunable in config/settings.py:
| Setting | Default | Purpose |
|---|---|---|
RAG_CHUNK_SIZE |
1000 |
Target characters per document chunk |
RAG_CHUNK_OVERLAP |
200 |
Overlap between consecutive chunks |
MAX_CHAT_HISTORY |
50 |
Max messages retained per session |
JWT lifetimes (SIMPLE_JWT): 60-minute access tokens, 1-day refresh tokens.
python manage.py testThe suite covers auth (registration/login/validation), grievance creation with mocked email, the RAG chunking / embedding / cosine-similarity helpers, and the health check. Embeddings are local and email is mocked, so no API key or network access is needed to run the tests.
This project is intentionally demo-scale. To take it to production:
- Embeddings: the local hashing embedding captures lexical overlap, not deep
semantics. For production-quality retrieval, swap
RAGProcessor._embed_textfor a real embedder (Voyage AI — Anthropic's recommended partner — or a local sentence-transformer). - Vector search: replace the in-Python cosine scan with a real vector index
(
pgvector, FAISS, or a managed vector DB) — the current approach is O(n) per query over all chunks. - Background processing: move document embedding off the request path into a task queue (Celery/RQ); it's currently synchronous.
- Channel layer: switch from the in-memory layer to
channels-redisso WebSockets work across multiple processes/workers. - Database: swap SQLite for PostgreSQL.
- Settings:
DEBUG=False, a strongDJANGO_SECRET_KEY, scopedALLOWED_HOSTS, HTTPS, and centralized logging/monitoring.
MIT — see LICENSE.