Asmitha-28's picture
Upload folder using huggingface_hub
f9f1893 verified
# dashboard/app.py
# SupportMind Streamlit Dashboard
# Asmitha · 2026
import streamlit as st
import sys, os, json
import numpy as np
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
st.set_page_config(page_title="SupportMind", page_icon="🧠", layout="wide")
# Custom CSS
st.markdown("""
<style>
.stApp { background-color: #0a0a0f; }
.metric-card { background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px; padding: 20px; text-align: center; }
.action-route { color: #22c55e; font-size: 28px; font-weight: 800; }
.action-clarify { color: #eab308; font-size: 28px; font-weight: 800; }
.action-escalate { color: #ef4444; font-size: 28px; font-weight: 800; }
</style>
""", unsafe_allow_html=True)
st.title("🧠 SupportMind")
st.caption("Confidence-Gated Support Intelligence for B2B SaaS")
# Sidebar
st.sidebar.header("⚙️ Configuration")
mc_passes = st.sidebar.slider("MC Dropout Passes", 5, 50, 20)
route_thresh = st.sidebar.slider("Route Threshold", 0.5, 0.95, 0.80, 0.05)
clarify_thresh = st.sidebar.slider("Clarify Threshold", 0.3, 0.8, 0.55, 0.05)
entropy_max = st.sidebar.slider("Max Entropy (Route)", 0.1, 1.0, 0.35, 0.05)
# Hero metrics
col1, col2, col3, col4 = st.columns(4)
col1.metric("Routing Accuracy", "89.1%", "+16.8pp")
col2.metric("Ambiguous Gain", "+32.3%", "vs baseline")
col3.metric("Annual Savings", "$756K", "per 10K tickets/mo")
col4.metric("Pipeline Latency", "45ms", "20-pass MC Dropout")
st.divider()
# Ticket Input
st.header("🎯 Route a Ticket")
presets = {
"Billing Issue": "My invoice from last month shows $299 but my plan is $199. Please fix this billing error immediately.",
"Technical Bug": "The API endpoint /v2/export returns a 500 error when batch size exceeds 1000 records.",
"Ambiguous Ticket": "Hey, we have issues with the export function since last Tuesday. Also our invoice looks incorrect. We are considering upgrading but want this sorted first.",
"Churn Risk": "This is the third time I'm reporting this. Still not fixed. We're looking at switching to a competitor.",
"Onboarding": "We just signed up yesterday and need help setting up SSO for our team of 50 users.",
}
preset = st.selectbox("Quick presets:", ["Custom"] + list(presets.keys()))
if preset != "Custom":
ticket_text = st.text_area("Ticket Text", value=presets[preset], height=100)
else:
ticket_text = st.text_area("Ticket Text", placeholder="Enter support ticket text...", height=100)
if st.button("⚡ Route Ticket", type="primary", use_container_width=True):
if ticket_text.strip():
with st.spinner("Running MC Dropout inference..."):
try:
from ensemble_router import EnsembleRouter
import ensemble_router as er
er.ROUTE_THRESHOLD = route_thresh
er.CLARIFY_THRESHOLD = clarify_thresh
er.ENTROPY_MAX = entropy_max
router = EnsembleRouter(device='cpu')
result = router.route(ticket_text, n_passes=mc_passes)
# Display action
action = result['action']
action_colors = {'route': '🟢', 'clarify': '🟡', 'escalate': '🔴'}
st.subheader(f"{action_colors.get(action, '')} Decision: {action.upper()}")
st.caption(result['reason'])
# Metrics
c1, c2, c3 = st.columns(3)
c1.metric("Confidence", f"{result['confidence']:.4f}")
c2.metric("Entropy", f"{result['entropy']:.4f}")
c3.metric("Top Category", result['top_category'].replace('_', ' ').title())
# Probability distribution
st.subheader("📊 Category Probabilities")
import plotly.graph_objects as go
cats = list(result['all_probs'].keys())
probs_vals = list(result['all_probs'].values())
fig = go.Figure(go.Bar(
x=probs_vals, y=[c.replace('_', ' ').title() for c in cats],
orientation='h',
marker_color=['#6366f1' if p == max(probs_vals) else '#334155' for p in probs_vals]
))
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
font_color='#94a3b8', height=300, margin=dict(l=0,r=0,t=10,b=0),
xaxis_title="Probability"
)
st.plotly_chart(fig, use_container_width=True)
# Clarification
if action == 'clarify':
st.subheader("💡 Suggested Clarification")
try:
from clarification_engine import ClarificationEngine
bank_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'clarification_bank.json')
clar = ClarificationEngine(bank_path)
probs_arr = np.array(list(result['all_probs'].values()))
q = clar.select_question(probs_arr, result['top_two_classes'])
st.info(f"**{q['question_text']}**")
for opt in q.get('options', []):
st.button(opt, disabled=True)
st.caption(f"Expected information gain: {q['expected_gain']:.4f}")
except Exception as e:
st.warning(f"Could not load clarification: {e}")
# SLA
st.subheader("🚨 SLA Breach Prediction")
try:
from sla_predictor import SLABreachPredictor
from feature_extraction import FeatureExtractor
feat_ext = FeatureExtractor()
features = feat_ext.extract(ticket_text)
sla_path = os.path.join(os.path.dirname(__file__), '..', 'models', 'sla_predictor', 'sla_xgb.json')
sla = SLABreachPredictor(sla_path)
sla_features = {
'text_complexity_score': features['text_complexity_score'],
'agent_queue_depth': 15, 'customer_tier': 3,
'hour_of_day': 14, 'day_of_week': 2,
'similar_ticket_avg_hrs': 4.5,
'sentiment_score': features['sentiment_score'],
'repeat_issue': 0, 'escalated_before': 0,
}
sla_result = sla.explain(sla_features)
sc1, sc2 = st.columns(2)
sc1.metric("Breach Probability", f"{sla_result['breach_probability']:.1%}")
sc2.metric("Risk Level", sla_result['risk_level'].upper())
if sla_result['contributing_factors']:
st.caption("Factors: " + ", ".join(sla_result['contributing_factors']))
except Exception as e:
st.warning(f"SLA prediction unavailable: {e}")
except Exception as e:
st.error(f"Error: {e}")
import traceback
st.code(traceback.format_exc())
else:
st.warning("Please enter ticket text.")