import streamlit as st
from agent import run_agent
from setup import setup
setup()
st.set_page_config(
page_title="SoapBox",
page_icon="✦",
layout="centered",
initial_sidebar_state="collapsed"
)
st.markdown("""
""", unsafe_allow_html=True)
# ── Session State ─────────────────────────────────────────────
if "stage" not in st.session_state:
st.session_state.stage = "upload"
if "uploaded_text" not in st.session_state:
st.session_state.uploaded_text = None
if "agent_steps" not in st.session_state:
st.session_state.agent_steps = []
if "final_note" not in st.session_state:
st.session_state.final_note = None
if "evaluation" not in st.session_state:
st.session_state.evaluation = None
TOOL_LABELS = {
"assess_completeness": "Assessed completeness",
"retrieve_similar_cases": "Searched knowledge base",
"check_medications": "Checked medications",
"generate_soap_note": "Generated SOAP note",
}
# ── Wordmark ──────────────────────────────────────────────────
st.markdown("# SoapBox")
st.markdown('
Your AI medical scribe
', unsafe_allow_html=True)
# ══════════════════════════════════════════════════════════════
# STAGE 1 — UPLOAD
# ══════════════════════════════════════════════════════════════
if st.session_state.stage == "upload":
st.markdown("""
""", unsafe_allow_html=True)
with st.container():
st.markdown("""
Paste your visit transcripts
Type or paste the provider — patient conversation below
""", unsafe_allow_html=True)
pasted_text = st.text_area(
"transcript",
height=220,
placeholder="Dr: Good morning, what brings you in today?\nPatient: I've been having neck pain for the past two weeks...",
label_visibility="collapsed"
)
# Button always visible
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
clicked = st.button("Begin Scribing",
disabled=not pasted_text.strip()
)
if clicked and pasted_text.strip():
st.session_state.uploaded_text = pasted_text
st.session_state.stage = "processing"
st.rerun()
# ══════════════════════════════════════════════════════════════
# STAGE 2 — PROCESSING
# ══════════════════════════════════════════════════════════════
elif st.session_state.stage == "processing":
tools_done = [
s["tool"] for s in st.session_state.agent_steps
if s["type"] == "tool_call"
]
pills = "".join([
f'✓ {TOOL_LABELS.get(t, t)}'
for t in tools_done
])
st.markdown(f"""
Scribing in progress
Processing transcript...
{pills}
""", unsafe_allow_html=True)
if not st.session_state.final_note:
steps = run_agent(st.session_state.uploaded_text)
st.session_state.agent_steps = steps
for step in steps:
if step["type"] == "final":
st.session_state.final_note = step["content"]
if step["type"] == "evaluation":
st.session_state.evaluation = step["content"]
st.session_state.stage = "complete"
st.rerun()
# ══════════════════════════════════════════════════════════════
# STAGE 3 — COMPLETE
# ══════════════════════════════════════════════════════════════
elif st.session_state.stage == "complete":
tools_used = len({
s["tool"] for s in st.session_state.agent_steps
if s["type"] == "tool_call"
})
ev = st.session_state.evaluation
tab1, tab2 = st.tabs(["SOAP Note", "Note Quality"])
# ══════════════════════════════════════════════════
# TAB 1 — SOAP Note
# ══════════════════════════════════════════════════
with tab1:
st.markdown(st.session_state.final_note)
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.download_button(
label="↓ Download Note",
data=st.session_state.final_note,
file_name="soap_note.txt",
mime="text/plain"
)
if st.button("↩ Scribe another"):
for key in ["uploaded_text", "agent_steps",
"final_note", "evaluation"]:
st.session_state[key] = None
st.session_state.stage = "upload"
st.rerun()
# ══════════════════════════════════════════════════
# TAB 2 — Note Quality (dynamic per session)
# ══════════════════════════════════════════════════
with tab2:
if not ev:
st.info("Evaluation not available for this note.")
else:
overall = ev.get("overall_score", 0)
if overall >= 9:
badge_color = "#1B4332"
badge_label = "Excellent"
elif overall >= 7:
badge_color = "#2D6A4F"
badge_label = "Good"
elif overall >= 5:
badge_color = "#B5924C"
badge_label = "Moderate"
else:
badge_color = "#9B1C1C"
badge_label = "Needs Review"
st.markdown(f"""
Note Quality Report
Evaluated against this transcript
{badge_label} · {overall}/10
Overall note quality
{overall}/10
""", unsafe_allow_html=True)
metrics_order = [
"completeness",
"accuracy",
"medication_capture",
"clinical_reasoning",
"structure"
]
metric_labels = {
"completeness": "Completeness",
"accuracy": "Accuracy",
"medication_capture": "Medication Capture",
"clinical_reasoning": "Clinical Reasoning",
"structure": "Structure"
}
for key in metrics_order:
if key not in ev:
continue
m = ev[key]
score = m.get("score", 0)
reason = m.get("reason", "")
description = m.get("description", "")
pct = int((score / 10) * 100)
if score >= 9:
bar_color = "#1B4332"
elif score >= 7:
bar_color = "#2D6A4F"
elif score >= 5:
bar_color = "#B5924C"
else:
bar_color = "#9B1C1C"
st.markdown(f"""
{metric_labels[key]}
{description}
{score}/10
{reason}
""", unsafe_allow_html=True)
st.markdown("""
For clinical oversight only
""", unsafe_allow_html=True)