File size: 4,553 Bytes
978fed5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""
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"<br><span style='font-size: 16px; font-weight: 600; color: #856404;'>{title}</span>"
        if title
        else ""
    )
    st.markdown(
        f"""<div style="background: #fff3cd; border-left: 4px solid #ffc107;
        padding: 12px 16px; border-radius: 6px; margin-bottom: 8px;">
        <span style="font-size: 18px; font-weight: 700;">⚠️ Approval required: {node_name}</span>
        {title_html}
        </div>""",
        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()