""" Display Components for Streamlit Interface Reusable components for displaying workflow outputs. """ import time import streamlit as st from scider.core.approval import ApprovalResponse, ApprovalResult def render_live_intermediate(items: list[dict]) -> None: """Render intermediate state items as they arrive (real-time).""" if not items: return for item in items: node = item.get("node_name", "unknown") output = item.get("output", "") with st.status(f"Node: {node}", state="complete"): st.markdown(output[:800] if output else "(no output)") _APPROVAL_CONTENT_TRUNCATE = 800 def render_approval_ui(handler) -> None: """Render approval buttons + feedback input for a pending approval request.""" pending = handler.get_pending() if not pending: return summary = pending["summary"] node_name = pending["node_name"] title = pending.get("title", "") # Highlighted header with title title_html = ( f"
{title}" if title else "" ) st.markdown( f"""
⚠️ Approval required: {node_name} {title_html}
""", unsafe_allow_html=True, ) # Approval content in a bordered container, collapsible if long if len(summary) > _APPROVAL_CONTENT_TRUNCATE: preview_lines = summary[:200].split("\n") label = " | ".join(line.strip() for line in preview_lines if line.strip())[:200] with st.expander(f"{label}...", expanded=True): st.markdown(summary) else: with st.container(border=True): st.markdown(summary) # Selection UI (radio buttons for single select) selected_index = None if pending.get("has_selection") and pending.get("items"): items = pending["items"] options = [f"{it.get('title', f'Item {i + 1}')}" for i, it in enumerate(items)] selected_label = st.radio("Select an idea:", options, key="idea_selection") selected_index = options.index(selected_label) # Show description of selected item selected_item = items[selected_index] if selected_item.get("description"): st.caption(selected_item["description"]) # Use a counter-based key so each new approval request gets a fresh input approval_count = st.session_state.get("_approval_count", 0) feedback_text = st.text_input( "Feedback (required for feedback option)", key=f"approval_feedback_input_{approval_count}", placeholder="Enter your feedback here...", ) # Buttons — use_container_width for equal sizing col1, col2, col3 = st.columns(3) with col1: label = "Approve selected" if selected_index is not None else "Approve" if st.button(f"✅ {label}", key="btn_approve", use_container_width=True): st.session_state.messages.append( {"role": "user", "content": f"✅ Approved [{node_name}]"} ) st.session_state["_approval_count"] = approval_count + 1 handler.submit_response( ApprovalResponse(ApprovalResult.APPROVED, selected_index=selected_index) ) st.rerun() with col2: if st.button("❌ Reject", key="btn_reject", use_container_width=True): st.session_state.messages.append( {"role": "user", "content": f"❌ Rejected [{node_name}]"} ) st.session_state["_approval_count"] = approval_count + 1 handler.submit_response(ApprovalResponse(ApprovalResult.REJECTED)) st.rerun() with col3: if st.button( "💬 Feedback", key="btn_feedback", use_container_width=True, disabled=not (feedback_text and feedback_text.strip()), ): st.session_state.messages.append( { "role": "user", "content": f"💬 Feedback [{node_name}]: {feedback_text.strip()}", } ) st.session_state["_approval_count"] = approval_count + 1 handler.submit_response( ApprovalResponse(ApprovalResult.FEEDBACK, feedback=feedback_text.strip()) ) st.rerun()