File size: 3,817 Bytes
0be7900 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | """
Shared UI components for the Distractor Annotation Tool.
Call render_sidebar() at the top of every page.
"""
import os
import streamlit as st
def render_sidebar(page_title: str = "") -> str:
"""
Render the common sidebar. Returns the current annotator name.
Uses a unique widget key per page to avoid Streamlit key collisions.
"""
with st.sidebar:
st.markdown("## π― Annotation Tool")
st.caption("NLP with DL β MSc Research")
st.divider()
# ββ Annotator identity ββββββββββββββββββββββββββββββββββββββββββββββ
current_name = st.session_state.get("annotator_name", "")
if current_name:
st.success(f"π€ **{current_name}**")
if st.button("βοΈ Change name", use_container_width=True):
st.session_state["annotator_name"] = ""
st.rerun()
else:
name = st.text_input(
"Your name",
placeholder="e.g. Alice",
# unique key per page avoids DuplicateWidgetID errors
key=f"sidebar_name__{page_title.replace(' ', '_')}",
)
if name.strip():
st.session_state["annotator_name"] = name.strip()
st.rerun()
else:
st.warning("β οΈ Enter your name to annotate")
st.divider()
# ββ Config status βββββββββββββββββββββββββββββββββββββββββββββββββββ
token_ok = bool(
os.environ.get("HF_TOKEN")
or (st.secrets.get("HF_TOKEN") if hasattr(st, "secrets") else None)
)
repo_ok = bool(
os.environ.get("ANNOTATIONS_REPO_ID")
or (st.secrets.get("ANNOTATIONS_REPO_ID") if hasattr(st, "secrets") else None)
)
if token_ok and repo_ok:
st.caption("π Connected to shared repo")
else:
if not token_ok:
st.error("HF_TOKEN missing")
if not repo_ok:
st.error("ANNOTATIONS_REPO_ID missing")
st.divider()
st.caption("π Links")
st.markdown("[Base Dataset](https://huggingface.co/datasets/nvidia/CantTalkAboutThis-Topic-Control-Dataset)")
st.markdown("[EMNLP Paper](https://aclanthology.org/2024.findings-emnlp.713)")
st.markdown("[arXiv:2511.05018](https://arxiv.org/abs/2511.05018)")
return st.session_state.get("annotator_name", "")
def render_conversation(conversation: list[dict], highlight_turn: str = "") -> None:
"""Render a conversation as chat bubbles. Highlights matching bot_turn if given."""
for turn in conversation:
role = turn.get("role", "user")
content = turn.get("content", "")
is_highlight = highlight_turn and role == "assistant" and content == highlight_turn
with st.chat_message(role):
if is_highlight:
st.markdown(f"**β¬οΈ Injection point:**\n\n{content}")
else:
st.write(content)
def render_distractor_multiturn(distractor: dict, idx: int) -> None:
"""Render a single multi-turn distractor entry."""
turns = distractor.get("turns", [])
st.markdown(f"**Subject:** {distractor.get('off_topic_subject', 'β')}")
st.markdown(f"**Tactic:** `{distractor.get('tactic_used', 'β')}`")
st.markdown(f"**Injected after:** _{distractor.get('bot_turn', 'β')[:80]}..._")
st.markdown(f"**Turns:** {len(turns)}")
with st.expander("Show turns"):
for t in turns:
with st.chat_message(t.get("role", "user")):
st.write(t.get("content", ""))
|