"""Theses tab: each agent's overnight picks, with committee verdicts badged inline.""" from __future__ import annotations import json import streamlit as st from agents.schemas import AgentOutput from orchestration.committee import load_verdicts from web.views.common import S, trade_dates _BADGE = {"approve": "✅", "reduce": "⚠️ reduced", "veto": "🚫 VETOED"} def _committee_memo(date: str) -> str: path = S.output_dir / date / "_committee.json" if not path.exists(): return "" try: return str(json.loads(path.read_text()).get("_memo", "")) except Exception: return "" def render() -> None: dates = trade_dates() if not dates: st.info("No nightly output yet. Run: `uv run python -m orchestration.run_nightly --compressed`") return date = st.selectbox("Trade date", dates) verdicts = load_verdicts(date) memo = _committee_memo(date) if memo: with st.expander("🏛️ Investment Committee memo", expanded=False): st.write(memo) for (agent, ticker), v in verdicts.items(): if v["verdict"] != "approve": st.caption(f"{_BADGE.get(v['verdict'], v['verdict'])} **{ticker}** ({agent}) — {v['reason']}") day_dir = S.output_dir / date files = sorted(f for f in day_dir.glob("*.json") if not f.name.startswith("_")) cols = st.columns(max(len(files), 1)) for col, f in zip(cols, files): try: out = AgentOutput.model_validate_json(f.read_text()) except Exception as e: col.error(f"{f.name}: {e}") continue with col: st.markdown(f"### {out.agent_name}") st.caption(f"status **{out.status.value}** · {len(out.picks)} picks · " f"{out.steps_used} steps · {out.wall_seconds:.0f}s") for p in out.picks: v = verdicts.get((out.persona_id, p.ticker)) badge = f" · {_BADGE[v['verdict']]}" if v and v["verdict"] != "approve" else "" inst = " · 🎯 option" if p.instrument.value == "option" else "" with st.expander(f"{p.ticker} · conv {p.conviction:.2f} · {p.time_horizon.value}{inst}{badge}"): st.write(p.thesis) if p.instrument.value == "option": st.caption(f"{p.option_right} {p.contract_symbol} · strike {p.strike} · exp {p.expiry}") st.caption( f"weight {p.requested_weight_pct:.0%} · stop {p.stop_loss_pct} · " f"take {p.take_profit_pct} · {p.direction.value}" ) if p.catalysts: st.caption("catalysts: " + ", ".join(p.catalysts)) if v and v["verdict"] != "approve": st.warning(f"committee: {v['verdict']} — {v['reason']}") if out.reasoning_trace: with st.expander("🔍 reasoning trace"): st.code("\n".join(out.reasoning_trace), language="text") if out.errors: st.warning("errors: " + "; ".join(out.errors))