Spaces:
Sleeping
Sleeping
| 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.") |