""" app.py — OpenEnv Hybrid AI Email & Calendar Assistant Streamlit Dashboard """ import os, sys, json, time import streamlit as st import pandas as pd sys.path.insert(0, os.path.dirname(__file__)) from env import EmailEnv, Action, SAMPLE_EMAILS from baseline import HybridAgent # ─── Page config ────────────────────────────────────────────────────────────── st.set_page_config( page_title="OpenEnv AI Email Assistant", page_icon="📬", layout="wide", ) st.markdown(""" """, unsafe_allow_html=True) # ─── Session state ───────────────────────────────────────────────────────────── if "env" not in st.session_state: st.session_state.env = EmailEnv() st.session_state.env.reset() if "agent" not in st.session_state: st.session_state.agent = HybridAgent() if "results" not in st.session_state: st.session_state.results = [] if "selected_email" not in st.session_state: st.session_state.selected_email = 0 if "last_action" not in st.session_state: st.session_state.last_action = None if "last_reward" not in st.session_state: st.session_state.last_reward = None env: EmailEnv = st.session_state.env agent: HybridAgent = st.session_state.agent emails = env._emails # ─── Sidebar ────────────────────────────────────────────────────────────────── with st.sidebar: st.title("📊 Performance") if st.session_state.results: scores = [r["score"] for r in st.session_state.results] avg_score = sum(scores) / len(scores) correct = sum(1 for r in st.session_state.results if r["action"] == r["expected"]) c1, c2 = st.columns(2) c1.metric("Avg Score", f"{avg_score:.2f}") c2.metric("Correct", f"{correct}/{len(scores)}") else: st.info("Run the agent to see metrics.") st.divider() st.subheader("🤖 Agent Mode") api_key = os.getenv("OPENAI_API_KEY", "") if api_key: st.success("OpenAI API active") else: st.warning("No API key — using HF/rules") mode_map = {"openai": "OpenAI GPT-4o-mini", "huggingface": "HuggingFace flan-t5", "rules": "Rule-based"} if st.session_state.last_action: st.info(f"Last mode: **{mode_map.get(agent.mode, agent.mode)}**") st.divider() if st.session_state.results: st.subheader("📈 Task Scores") for i, r in enumerate(st.session_state.results): color = "green" if r["score"] > 0 else "red" st.markdown(f"Task {r['task']}: **:{color}[{r['score']:.2f}]**") if st.button("🔄 Reset Environment"): env.reset() st.session_state.results = [] st.session_state.last_action = None st.session_state.last_reward = None st.rerun() # ─── Main Layout ─────────────────────────────────────────────────────────────── st.title("📬 OpenEnv Hybrid AI Email & Calendar Assistant") st.caption("Powered by OpenAI GPT-4o-mini • HuggingFace flan-t5-base • Rule-based fallback") col_inbox, col_main = st.columns([1, 2]) # ─── Inbox Panel ────────────────────────────────────────────────────────────── with col_inbox: st.subheader("📬 Inbox") for i, email in enumerate(emails): p = email["priority"] badge_class = f"priority-{p}" is_selected = i == st.session_state.selected_email border = "2px solid #4299E1" if is_selected else "1px solid #E2E8F0" with st.container(): clicked = st.button( f"{'📌' if p == 'urgent' else '📧'} {email['subject'][:35]}...", key=f"email_{i}", use_container_width=True, ) if clicked: st.session_state.selected_email = i st.session_state.last_action = None st.session_state.last_reward = None st.rerun() st.markdown( f"{p} · {email['sender']}", unsafe_allow_html=True, ) st.markdown("---") # ─── Main Panel ─────────────────────────────────────────────────────────────── with col_main: sel_idx = st.session_state.selected_email sel_email = emails[sel_idx] # Email Viewer st.subheader("📧 Email Viewer") with st.container(): p = sel_email["priority"] st.markdown( f"**From:** {sel_email['sender']} " f"{p}", unsafe_allow_html=True, ) st.markdown(f"**Subject:** {sel_email['subject']}") st.markdown(f"**Received:** {sel_email['timestamp'][:19]}") st.divider() st.write(sel_email["body"]) st.divider() # Agent Panel st.subheader("🤖 AI Agent Panel") run_btn = st.button("▶️ Run AI Assistant", type="primary", use_container_width=True) if run_btn: with st.spinner("Running hybrid AI agent (this may take a few seconds on CPU)..."): action_dict, mode = agent.decide( sender=sel_email["sender"], subject=sel_email["subject"], body=sel_email["body"], priority=sel_email["priority"], ) action = Action(**action_dict) # Temporarily set env index to current email env._index = sel_idx _, reward, _, info = env.step(action) st.session_state.last_action = {**action_dict, "mode": mode} st.session_state.last_reward = {"score": reward.score, "reason": reward.reason} result_entry = { "task": sel_idx + 1, "subject": sel_email["subject"][:40], "difficulty": ["easy", "easy", "easy", "medium", "hard"][sel_idx], "expected": sel_email["expected_action"], "action": action.action_type, "score": reward.score, "mode": mode, "calendar_update": info.get("calendar_update"), } existing = [r for r in st.session_state.results if r["task"] != sel_idx + 1] st.session_state.results = existing + [result_entry] if st.session_state.last_action: la = st.session_state.last_action lr = st.session_state.last_reward at = la.get("action_type", "—") content = la.get("content", "") proposed = la.get("proposed_time", "") st.markdown(f"""
{at} | Mode: {la.get('mode','—')} {f" | Proposed time: {proposed}" if proposed else ""}