# Portions of this file were developed with assistance from OpenAI ChatGPT/Codex and reviewed/modified by the author.
"""Streamlit investor-demo app for campus support triage.
Portions of this file were developed with assistance from OpenAI ChatGPT/Codex and reviewed/modified by the author.
"""
from __future__ import annotations
import html
import sys
from pathlib import Path
SRC_DIR = Path(__file__).resolve().parents[1]
ROOT_DIR = SRC_DIR.parent
for import_path in (SRC_DIR, ROOT_DIR):
if str(import_path) not in sys.path:
sys.path.insert(0, str(import_path))
import streamlit as st
from campus_triage.config import CATEGORY_LABELS, URGENCY_LABELS
from campus_triage.predict import EXAMPLE_MESSAGES, load_deployed_model, model_available, model_search_diagnostics, predict_message
CUSTOM_CSS = """
"""
def display_label(label: str) -> str:
"""Convert model labels into polished display text."""
return label.replace("_", " ").title()
def confidence_band(score: float) -> str:
"""Convert confidence into a product-facing review band."""
if score >= 0.75:
return "Auto-route candidate"
if score >= 0.55:
return "Route with review"
return "Manual review recommended"
def urgency_class(urgency: str) -> str:
"""Return the CSS class for an urgency pill."""
return {
"high": "urgency-high",
"medium": "urgency-medium",
"low": "urgency-low",
}.get(urgency, "urgency-medium")
def render_status_strip() -> None:
"""Render investor-demo operating metrics."""
st.markdown(
"""
Urgency Tiers
Low / Medium / High
Default Model
TF-IDF Logistic Regression
""",
unsafe_allow_html=True,
)
def render_score_table(scores: dict[str, float], labels: list[str]) -> None:
"""Render confidence scores as a compact leaderboard."""
ordered_labels = sorted(labels, key=lambda label: scores.get(label, 0.0), reverse=True)
for label in ordered_labels:
score = max(0.0, min(scores.get(label, 0.0), 1.0))
st.markdown(
f"""
{html.escape(display_label(label))}
{score:.0%}
""",
unsafe_allow_html=True,
)
def render_example_buttons() -> None:
"""Render example messages as quick-fill controls."""
st.caption("Demo-ready examples")
for index, example in enumerate(EXAMPLE_MESSAGES, start=1):
label = f"Example {index}: {example[:54]}{'...' if len(example) > 54 else ''}"
if st.button(label, key=f"example_{index}", use_container_width=True):
st.session_state["message_text"] = example
def render_empty_decision_panel() -> None:
"""Render the pre-analysis decision panel."""
st.markdown('Triage Decision
Run an analysis to generate routing output, confidence, and next action.
', unsafe_allow_html=True)
st.markdown(
"""
Current Status
Ready
Model loadedSynthetic POC
""",
unsafe_allow_html=True,
)
st.markdown("
", unsafe_allow_html=True)
def render_prediction(prediction: dict[str, object]) -> None:
"""Render prediction cards, recommendation, and confidence evidence."""
category = str(prediction["category"])
urgency = str(prediction["urgency"])
category_confidence = float(prediction["category_confidence"])
urgency_confidence = float(prediction["urgency_confidence"])
band = confidence_band(category_confidence)
st.markdown('Triage Decision
Operational output for the support intake queue.
', unsafe_allow_html=True)
result_columns = st.columns(3)
with result_columns[0]:
st.markdown(
f"""
Predicted Queue
{html.escape(display_label(category))}
{category_confidence:.0%} confidence
""",
unsafe_allow_html=True,
)
with result_columns[1]:
st.markdown(
f"""
Urgency Tier
{html.escape(display_label(urgency))}
{urgency_confidence:.0%} confidence
""",
unsafe_allow_html=True,
)
with result_columns[2]:
st.markdown(
f"""
Review Posture
{html.escape(band)}
Human-in-loop
""",
unsafe_allow_html=True,
)
st.markdown(
f"""
Recommended next action: {html.escape(str(prediction["routing_recommendation"]))}
Why this route: {html.escape(str(prediction["explanation"]))}
""",
unsafe_allow_html=True,
)
st.markdown("
", unsafe_allow_html=True)
score_columns = st.columns(2)
with score_columns[0]:
st.markdown('Category Confidence
Ranked routing probabilities.
', unsafe_allow_html=True)
render_score_table(prediction["category_scores"], CATEGORY_LABELS) # type: ignore[arg-type]
st.markdown("
", unsafe_allow_html=True)
with score_columns[1]:
st.markdown('Urgency Confidence
Ranked urgency probabilities.
', unsafe_allow_html=True)
render_score_table(prediction["urgency_scores"], URGENCY_LABELS) # type: ignore[arg-type]
st.markdown("
", unsafe_allow_html=True)
def run_app() -> None:
"""Run the Streamlit application."""
st.set_page_config(page_title="Campus Triage Assistant", page_icon="CS", layout="wide")
st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
st.markdown(
"""
Campus Support Message Triage Assistant
AI-assisted intake for university support teams: classify incoming student messages, estimate urgency, and produce routing guidance with confidence evidence.
""",
unsafe_allow_html=True,
)
render_status_strip()
if not model_available():
st.error("No trained model artifact was found for inference.")
st.caption("The app checked these local and Hugging Face deployment paths:")
st.code(model_search_diagnostics())
st.stop()
model = load_deployed_model()
if "message_text" not in st.session_state:
st.session_state["message_text"] = EXAMPLE_MESSAGES[0]
intake_column, decision_column = st.columns([1.05, 1.15], gap="large")
with intake_column:
st.markdown('Message Intake
Paste a support request or load a demo scenario.
', unsafe_allow_html=True)
render_example_buttons()
message_text = st.text_area(
"Student message",
key="message_text",
height=210,
label_visibility="collapsed",
)
submitted = st.button("Analyze and Route Message", type="primary", use_container_width=True)
st.markdown(
"""
""",
unsafe_allow_html=True,
)
st.markdown("
", unsafe_allow_html=True)
with decision_column:
if submitted and message_text.strip():
prediction = predict_message(message_text, model=model)
render_prediction(prediction)
elif submitted:
st.warning("Paste a student message before analyzing.")
render_empty_decision_panel()
else:
render_empty_decision_panel()
st.markdown(
"""
""",
unsafe_allow_html=True,
)
if __name__ == "__main__":
run_app()