🧠 AI Ticket Classifier
Smart ticket grouping using TF-IDF & Cosine Similarity — powered by Python
import streamlit as st import pandas as pd import numpy as np import re from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity # ── Page Configuration ────────────────────────────────────────────────────────── st.set_page_config( page_title="AI Ticket Classifier", page_icon="🧠", layout="wide", initial_sidebar_state="collapsed" ) # ── Custom CSS for Premium Dark Theme ──────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ── Knowledge Base ─────────────────────────────────────────────────────────────── CATEGORIES = { "password": { "name": "🔐 Password / Authentication", "icon": "🔐", "css": "password", "color": "#6366f1", "keywords": [ "password", "passwd", "reset", "forgot", "login", "log in", "sign in", "signin", "authenticate", "credentials", "locked out", "lockout", "incorrect password", "wrong password", "cant log", "can't log", "unable to login", "access denied", "two factor", "2fa", "mfa", "otp" ], "response": ( "🔐 **Password / Authentication Help**\n\n" "To reset your password:\n" "1. Go to the login page and click **Forgot Password**\n" "2. Enter your registered email address\n" "3. Check your inbox for the reset link (also check spam)\n" "4. Create a new password (min 8 chars, 1 uppercase, 1 number)\n\n" "If your account is locked, please wait 15 minutes or contact IT support at ext. 1234." ) }, "hr": { "name": "📊 HR / Leave Management", "icon": "📊", "css": "hr", "color": "#10b981", "keywords": [ "leave", "leave balance", "vacation", "holiday", "sick leave", "casual leave", "attendance", "absence", "time off", "pto", "paid time off", "work from home", "wfh", "remote work", "salary", "payslip", "pay slip", "payroll", "bonus", "appraisal", "hr", "human resource", "benefits", "insurance", "medical", "bank account", "bank details", "compensation" ], "response": ( "📊 **HR / Leave Management Help**\n\n" "To check your leave balance:\n" "1. Log in to the HR Portal (hr.company.com)\n" "2. Navigate to **My Leave** → **Leave Balance**\n" "3. View category-wise balance (Casual, Sick, Earned)\n\n" "To apply for leave or WFH, go to My Leave → Apply Leave. " "For salary/payroll queries, contact hr@company.com." ) }, "it": { "name": "💻 IT / Hardware Support", "icon": "💻", "css": "it", "color": "#f59e0b", "keywords": [ "laptop", "computer", "slow", "crash", "hang", "freeze", "blue screen", "bsod", "restart", "reboot", "software", "install", "update", "upgrade", "printer", "scanner", "monitor", "keyboard", "mouse", "hardware", "performance", "disk", "storage", "memory", "ram" ], "response": ( "💻 **IT / Hardware Support Help**\n\n" "For hardware issues:\n" "1. Try restarting your device first\n" "2. Clear temporary files: Win+R → type 'temp' → delete all\n" "3. Check Task Manager (Ctrl+Shift+Esc) for high CPU/memory usage\n\n" "If the issue persists, raise a ticket at it-support.company.com or call ext. 5678." ) }, "network": { "name": "🌐 Network / Connectivity", "icon": "🌐", "css": "network", "color": "#06b6d4", "keywords": [ "vpn", "network", "internet", "wifi", "wi-fi", "connect", "connection", "disconnect", "firewall", "proxy", "bandwidth", "speed", "slow internet", "office network", "remote access", "rdp", "remote desktop", "intranet", "server", "dns" ], "response": ( "🌐 **Network / Connectivity Help**\n\n" "For VPN/network issues:\n" "1. Disconnect and reconnect your VPN client\n" "2. Ensure you're using the latest VPN client version\n" "3. Try switching between Wi-Fi and wired connection\n" "4. Restart your router/modem if working from home\n\n" "For persistent issues, contact Network Operations at ext. 9012." ) }, "access": { "name": "🔑 Access / Permissions", "icon": "🔑", "css": "access", "color": "#a855f7", "keywords": [ "access", "permission", "role", "shared drive", "folder", "repository", "repo", "github", "gitlab", "jira", "confluence", "admin", "privileges", "unauthorized", "forbidden", "restricted", "grant access", "request access", "onboarding" ], "response": ( "🔑 **Access / Permissions Help**\n\n" "To request access:\n" "1. Go to the Access Management Portal (access.company.com)\n" "2. Click **New Access Request**\n" "3. Search for the resource (shared drive, app, repo)\n" "4. Select the required role/permission level\n" "5. Submit — your manager will receive an approval request\n\n" "For urgent access needs, contact your IT admin directly." ) }, "general": { "name": "❓ General Inquiry", "icon": "❓", "css": "general", "color": "#64748b", "keywords": [], "response": ( "❓ **General Inquiry**\n\n" "Thank you for reaching out! Your ticket has been received " "and will be reviewed by our support team.\n\n" "Expected response time: 2–4 business hours.\n\n" "For urgent issues, please call the helpdesk at ext. 1111." ) } } # ── Stop Words ─────────────────────────────────────────────────────────────────── STOP_WORDS = { "i", "me", "my", "myself", "we", "our", "you", "your", "he", "she", "it", "its", "they", "them", "what", "which", "who", "this", "that", "am", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "do", "does", "did", "a", "an", "the", "and", "but", "if", "or", "because", "as", "until", "while", "of", "at", "by", "for", "with", "about", "between", "through", "during", "before", "after", "to", "from", "up", "down", "in", "out", "on", "off", "over", "under", "again", "then", "once", "here", "there", "when", "where", "why", "how", "all", "both", "each", "few", "more", "most", "other", "some", "no", "not", "only", "own", "same", "so", "than", "too", "very", "can", "will", "just", "don", "should", "now", "please" } # ── NLP Functions ──────────────────────────────────────────────────────────────── def preprocess(text: str) -> str: """Lowercase, remove punctuation, remove stop words.""" text = text.lower() text = re.sub(r"[^a-z0-9\s'-]", " ", text) tokens = text.split() tokens = [t for t in tokens if t not in STOP_WORDS and len(t) > 1] return " ".join(tokens) def classify_ticket(text: str) -> dict: """Classify a ticket into a category using keyword matching.""" lower = text.lower() best_cat_id = "general" best_score = 0 matched_keywords = [] for cat_id, cat in CATEGORIES.items(): if not cat["keywords"]: continue score = 0 matched = [] for kw in cat["keywords"]: if kw in lower: weight = len(kw.split()) * 2 score += weight matched.append(kw) if score > best_score: best_score = score best_cat_id = cat_id matched_keywords = list(set(matched)) # Sigmoid-like confidence mapping confidence = 0.15 if best_score == 0 else min(0.99, 1 - 1 / (1 + best_score * 0.4)) return { "category_id": best_cat_id, "category": CATEGORIES[best_cat_id], "confidence": confidence, "matched_keywords": matched_keywords } def compute_similarity_matrix(texts: list) -> np.ndarray: """Compute pairwise cosine similarity using TF-IDF.""" if len(texts) < 2: return np.array([[1.0]]) processed = [preprocess(t) for t in texts] vectorizer = TfidfVectorizer() tfidf_matrix = vectorizer.fit_transform(processed) return cosine_similarity(tfidf_matrix) def sim_color(val: float) -> str: """Map similarity score to a background colour.""" if val > 0.8: return "rgba(16, 185, 129, 0.7)" if val > 0.6: return "rgba(16, 185, 129, 0.45)" if val > 0.4: return "rgba(99, 102, 241, 0.45)" if val > 0.2: return "rgba(99, 102, 241, 0.25)" return "rgba(255,255,255, 0.04)" # ── Session State ──────────────────────────────────────────────────────────────── if "tickets" not in st.session_state: # Pre-load the 3 example tickets examples = [ "I forgot my password, how to reset it?", "I can't log in, as password is incorrect", "How to see leave balance?" ] st.session_state.tickets = [] for i, text in enumerate(examples): result = classify_ticket(text) st.session_state.tickets.append({ "id": i + 1, "text": text, **result }) st.session_state.next_id = 4 # ── Header ─────────────────────────────────────────────────────────────────────── st.markdown("""
Smart ticket grouping using TF-IDF & Cosine Similarity — powered by Python
No tickets submitted yet.
Submit a ticket to see how they get grouped!
| ' for t in tickets: html += f' | #{t["id"]} | ' html += '
|---|---|
| #{t["id"]} | ' for j in range(len(tickets)): val = sim_matrix[i][j] pct = int(val * 100) bg = sim_color(val) txt_color = "#fff" if val > 0.5 else "#c4c7d9" html += f'{pct}% | ' html += '