Shami96 commited on
Commit
f6e88dd
Β·
verified Β·
1 Parent(s): e93c1a2

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +101 -34
src/streamlit_app.py CHANGED
@@ -1,40 +1,107 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
 
 
 
 
 
 
 
 
 
 
 
4
  import streamlit as st
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
 
 
 
 
 
 
 
 
 
 
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
 
 
 
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
 
 
 
 
 
 
14
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ import os
2
+
3
+ # Force Streamlit to use /tmp for config, cache, metrics
4
+ os.environ["HOME"] = "/tmp"
5
+ os.environ["XDG_CONFIG_HOME"] = "/tmp"
6
+ os.environ["XDG_CACHE_HOME"] = "/tmp"
7
+ os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
8
+
9
+ # Create /tmp/.streamlit manually so Streamlit doesn't try to write to /
10
+ os.makedirs("/tmp/.streamlit", exist_ok=True)
11
+
12
+ # --- now import the rest ---
13
+ import json
14
+ from datetime import datetime
15
  import streamlit as st
16
+ from supabase import create_client
17
+ from openai import OpenAI
18
 
19
+ # --- env vars ---
20
+ SUPABASE_URL = os.environ["SUPABASE_URL"]
21
+ SUPABASE_SERVICE_ROLE_KEY = os.environ["SUPABASE_SERVICE_ROLE_KEY"]
22
+ OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
23
+
24
+ sb = create_client(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
25
+ client = OpenAI(api_key=OPENAI_API_KEY)
26
+
27
+ # --- UI ---
28
+ st.set_page_config(page_title="Email β†’ LLM β†’ Supabase Demo", layout="centered")
29
+ st.title("πŸ“§ Email β†’ πŸ€– LLM β†’ πŸ“¦ Supabase")
30
+ st.caption("Paste an email, classify with LLM, store in Supabase, approve or decline the action.")
31
 
32
+ raw = st.text_area("Email body", height=200, placeholder="Paste email text here…")
33
+ col1, col2 = st.columns(2)
34
+ with col1:
35
+ subject = st.text_input("Subject (optional)")
36
+ with col2:
37
+ sender = st.text_input("From (optional)")
38
 
39
+ # --- helpers ---
40
+ def classify_email(email_text: str) -> dict:
41
+ prompt = f"""
42
+ Classify the email as one of: offer_update, rental_request, general.
43
+ Return STRICT JSON: {{"label":"...","confidence":0.0,"suggestion":"..."}}
44
+ Email:
45
+ {email_text}
46
  """
47
+ resp = client.chat.completions.create(
48
+ model="gpt-4o-mini",
49
+ temperature=0.2,
50
+ messages=[{"role": "user", "content": prompt}],
51
+ )
52
+ text = resp.choices[0].message.content or "{}"
53
+ try:
54
+ start, end = text.find("{"), text.rfind("}") + 1
55
+ return json.loads(text[start:end])
56
+ except Exception:
57
+ return {"label": "general", "confidence": 0.6, "suggestion": "Acknowledge receipt."}
58
+
59
+ def insert_email(raw_text, subject, from_addr):
60
+ res = sb.table("emails").insert(
61
+ {"raw_text": raw_text, "subject": subject, "from_addr": from_addr}
62
+ ).execute()
63
+ return res.data[0]
64
+
65
+ def insert_classification(email_id, cls):
66
+ sb.table("classifications").insert({
67
+ "email_id": email_id,
68
+ "label": cls["label"],
69
+ "confidence": cls["confidence"],
70
+ "suggestion": cls["suggestion"],
71
+ }).execute()
72
+
73
+ def approve_action(email_id, suggestion):
74
+ sb.table("action_log").insert({
75
+ "email_id": email_id,
76
+ "action": suggestion,
77
+ "approved": True,
78
+ "approved_at": datetime.utcnow().isoformat(),
79
+ }).execute()
80
+
81
+ # --- flow ---
82
+ if st.button("πŸ”Ž Classify and Save", type="primary", disabled=not raw.strip()):
83
+ try:
84
+ email_row = insert_email(raw, subject, sender)
85
+ cls = classify_email(raw)
86
+ insert_classification(email_row["id"], cls)
87
+ st.session_state["current"] = {"email": email_row, "cls": cls}
88
+ st.success("βœ… Email classified and saved.")
89
+ except Exception as e:
90
+ st.error(f"Error: {e}")
91
+
92
+ current = st.session_state.get("current")
93
+ if current:
94
+ st.subheader("Result")
95
+ st.write(f"**Label:** {current['cls']['label']}")
96
+ st.write(f"**Confidence:** {current['cls']['confidence']:.2f}")
97
+ st.write("**Suggested action:**")
98
+ st.code(current["cls"]["suggestion"])
99
 
100
+ if st.button("πŸ‘ Approve"):
101
+ try:
102
+ approve_action(current["email"]["id"], current["cls"]["suggestion"])
103
+ st.success("Approved and logged.")
104
+ except Exception as e:
105
+ st.error(f"Error approving: {e}")
106
+ if st.button("πŸ‘Ž Decline"):
107
+ st.info("Declined. No action logged.")