"""Página de aprobaciones pendientes (HITL).""" from __future__ import annotations import streamlit as st from agentforge_dashboard.client import CoreClient @st.cache_resource def get_client() -> CoreClient: return CoreClient() client = get_client() st.title("🤝 Aprobaciones pendientes") st.caption("Ejecuciones pausadas en HITL. Revisa cada acción propuesta y aprueba o rechaza.") executions = client.list_executions() pending = [e for e in executions if e["status"] == "awaiting_approval"] if not pending: st.success("No hay aprobaciones pendientes.") st.stop() selected = st.selectbox( "Ejecución", pending, format_func=lambda e: f"{e['trace_id'][:8]} — {e['agent_name']} @ {e['agent_version']}", ) execution = client.get_execution(selected["trace_id"]) st.markdown(f"**trace_id:** `{execution['trace_id']}`") st.markdown(f"**Agente:** `{execution['agent_name']}@{execution['agent_version']}`") st.markdown(f"**Iniciada:** {execution['started_at']}") if execution.get("final_output"): with st.expander("Output bruto del LLM"): st.json(execution["final_output"]) st.subheader("Acciones propuestas (requieren aprobación)") needs = execution.get("needs_human_for") or [] if not needs: st.info("No hay acciones que requieran aprobación.") st.stop() approved_ids: list[str] = [] for action in needs: with st.container(border=True): risk = action["risk_score"] risk_color = {1: "🟩", 2: "🟩", 3: "🟨", 4: "🟧", 5: "🟥"}.get(risk, "⬜") st.markdown(f"### {risk_color} `{action['action']}` → `{action['target']}`") st.write(f"**Risk score:** {risk}/5 | Requires approval: `{action['requires_approval']}`") st.write(f"**Rollback plan:** {action['rollback_plan']}") if st.checkbox("Aprobar esta acción", key=f"chk_{action['id']}"): approved_ids.append(action["id"]) comment = st.text_input("Comentario (opcional)") col_a, col_r = st.columns(2) with col_a: if st.button("✅ Aprobar seleccionadas", type="primary", disabled=not approved_ids): with st.spinner("Aplicando aprobación..."): r = client.approve( execution["trace_id"], {"approved_action_ids": approved_ids, "comment": comment}, ) if "error" in r: st.error(r["error"]) else: st.success(f"Status: {r['status']}") st.rerun() with col_r: reason = st.text_input("Razón de rechazo") if st.button("❌ Rechazar ejecución", disabled=not reason): with st.spinner("Aplicando rechazo..."): r = client.reject(execution["trace_id"], {"reason": reason}) if "error" in r: st.error(r["error"]) else: st.success(f"Status: {r['status']}") st.rerun()