File size: 3,680 Bytes
f6e88dd
 
 
 
 
 
 
 
 
 
 
 
 
 
b644e58
f6e88dd
 
b644e58
f6e88dd
 
 
 
 
 
 
 
 
 
 
 
b644e58
f6e88dd
 
 
 
 
 
b644e58
f6e88dd
 
 
 
 
 
 
b644e58
f6e88dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b644e58
f6e88dd
 
 
 
 
 
 
 
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
import os

# Force Streamlit to use /tmp for config, cache, metrics
os.environ["HOME"] = "/tmp"
os.environ["XDG_CONFIG_HOME"] = "/tmp"
os.environ["XDG_CACHE_HOME"] = "/tmp"
os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"

# Create /tmp/.streamlit manually so Streamlit doesn't try to write to /
os.makedirs("/tmp/.streamlit", exist_ok=True)

# --- now import the rest ---
import json
from datetime import datetime
import streamlit as st
from supabase import create_client
from openai import OpenAI

# --- env vars ---
SUPABASE_URL = os.environ["SUPABASE_URL"]
SUPABASE_SERVICE_ROLE_KEY = os.environ["SUPABASE_SERVICE_ROLE_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

sb = create_client(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
client = OpenAI(api_key=OPENAI_API_KEY)

# --- UI ---
st.set_page_config(page_title="Email β†’ LLM β†’ Supabase Demo", layout="centered")
st.title("πŸ“§ Email β†’ πŸ€– LLM β†’ πŸ“¦ Supabase")
st.caption("Paste an email, classify with LLM, store in Supabase, approve or decline the action.")

raw = st.text_area("Email body", height=200, placeholder="Paste email text here…")
col1, col2 = st.columns(2)
with col1:
    subject = st.text_input("Subject (optional)")
with col2:
    sender = st.text_input("From (optional)")

# --- helpers ---
def classify_email(email_text: str) -> dict:
    prompt = f"""
Classify the email as one of: offer_update, rental_request, general.
Return STRICT JSON: {{"label":"...","confidence":0.0,"suggestion":"..."}}
Email:
{email_text}
"""
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        temperature=0.2,
        messages=[{"role": "user", "content": prompt}],
    )
    text = resp.choices[0].message.content or "{}"
    try:
        start, end = text.find("{"), text.rfind("}") + 1
        return json.loads(text[start:end])
    except Exception:
        return {"label": "general", "confidence": 0.6, "suggestion": "Acknowledge receipt."}

def insert_email(raw_text, subject, from_addr):
    res = sb.table("emails").insert(
        {"raw_text": raw_text, "subject": subject, "from_addr": from_addr}
    ).execute()
    return res.data[0]

def insert_classification(email_id, cls):
    sb.table("classifications").insert({
        "email_id": email_id,
        "label": cls["label"],
        "confidence": cls["confidence"],
        "suggestion": cls["suggestion"],
    }).execute()

def approve_action(email_id, suggestion):
    sb.table("action_log").insert({
        "email_id": email_id,
        "action": suggestion,
        "approved": True,
        "approved_at": datetime.utcnow().isoformat(),
    }).execute()

# --- flow ---
if st.button("πŸ”Ž Classify and Save", type="primary", disabled=not raw.strip()):
    try:
        email_row = insert_email(raw, subject, sender)
        cls = classify_email(raw)
        insert_classification(email_row["id"], cls)
        st.session_state["current"] = {"email": email_row, "cls": cls}
        st.success("βœ… Email classified and saved.")
    except Exception as e:
        st.error(f"Error: {e}")

current = st.session_state.get("current")
if current:
    st.subheader("Result")
    st.write(f"**Label:** {current['cls']['label']}")
    st.write(f"**Confidence:** {current['cls']['confidence']:.2f}")
    st.write("**Suggested action:**")
    st.code(current["cls"]["suggestion"])

    if st.button("πŸ‘ Approve"):
        try:
            approve_action(current["email"]["id"], current["cls"]["suggestion"])
            st.success("Approved and logged.")
        except Exception as e:
            st.error(f"Error approving: {e}")
    if st.button("πŸ‘Ž Decline"):
        st.info("Declined. No action logged.")