Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
| 3 |
import requests
|
| 4 |
import json
|
| 5 |
from datetime import datetime
|
| 6 |
|
|
|
|
|
|
|
| 7 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 8 |
GROQ_COMPLETION_URL = "https://api.groq.com/openai/v1/chat/completions"
|
| 9 |
GROQ_MODEL = "llama-3.1-8b-instant"
|
|
@@ -28,12 +32,13 @@ def groq_completion(prompt, sys_prompt=None):
|
|
| 28 |
response = requests.post(GROQ_COMPLETION_URL, headers=headers, json=body)
|
| 29 |
response.raise_for_status()
|
| 30 |
return response.json()["choices"][0]["message"]["content"]
|
| 31 |
-
except Exception
|
| 32 |
-
return "
|
| 33 |
|
| 34 |
def email_classifier_router(raw_email):
|
| 35 |
prompt = (
|
| 36 |
-
"
|
|
|
|
| 37 |
"\n- Category (Support Request, Sales Inquiry, Finance/Billing, Urgent Incident, Spam/Marketing)"
|
| 38 |
"\n- Priority (Low, Medium, High)"
|
| 39 |
"\n- Suggested Recipient"
|
|
@@ -44,16 +49,29 @@ def email_classifier_router(raw_email):
|
|
| 44 |
f"{raw_email}"
|
| 45 |
)
|
| 46 |
raw_result = groq_completion(prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
try:
|
| 48 |
output = json.loads(raw_result)
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
output = {
|
| 51 |
-
"Category": "
|
| 52 |
-
"Priority": "
|
| 53 |
-
"Suggested Recipient": "
|
| 54 |
-
"Draft Response": "",
|
| 55 |
-
"Summary": "
|
| 56 |
-
"Action Items": ["
|
| 57 |
}
|
| 58 |
return output
|
| 59 |
|
|
@@ -71,6 +89,8 @@ def add_to_history(email, cat, pri, summ, dra):
|
|
| 71 |
css="""
|
| 72 |
body { background: linear-gradient(120deg, #10193a 0%, #175ad7 120%) !important; font-family: 'Inter', 'Segoe UI', Arial, sans-serif;}
|
| 73 |
.gradio-container { background: transparent !important; }
|
|
|
|
|
|
|
| 74 |
.card-block { background: rgba(27,36,68,0.96); border-radius: 30px; box-shadow: 0 6px 42px #0b2269bb, 0 1.5px 0 #186fc055 inset; padding: 38px 28px 29px 28px; margin: 32px 0; color: #fafeff; border: 2px solid #2847a46c; backdrop-filter: blur(8px);}
|
| 75 |
input, textarea, .wrap.svelte-psy7la, .wrap.svelte-1ybjah6 { background: rgba(21,28,56,0.91)!important; border:1.5px solid #345ca3e3!important; color:#def1ff!important; border-radius: 14px !important; box-shadow: none !important;}
|
| 76 |
label, .label.svelte-1ybjah6 { color: #70acf8!important; font-size: 1em !important; font-weight: 400;}
|
|
@@ -92,7 +112,13 @@ label, .label.svelte-1ybjah6 { color: #70acf8!important; font-size: 1em !importa
|
|
| 92 |
"""
|
| 93 |
|
| 94 |
with gr.Blocks(css=css) as demo:
|
| 95 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
with gr.Row():
|
| 97 |
with gr.Column(scale=6, min_width=420):
|
| 98 |
with gr.Group(elem_id="input-card", elem_classes=["card-block"]):
|
|
@@ -153,8 +179,9 @@ with gr.Blocks(css=css) as demo:
|
|
| 153 |
acts = result.get("Action Items", [])
|
| 154 |
acts = "\n".join(acts) if isinstance(acts, list) else acts
|
| 155 |
add_to_history(email_text, cat, pri, summ, dra)
|
| 156 |
-
badge =
|
| 157 |
-
|
|
|
|
| 158 |
# Only show last 5 records
|
| 159 |
recent_history = classification_history[-5:][::-1]
|
| 160 |
history_html = "<div class='history-cardrow'>"
|
|
@@ -174,7 +201,7 @@ with gr.Blocks(css=css) as demo:
|
|
| 174 |
</div>
|
| 175 |
"""
|
| 176 |
history_html += "</div>"
|
| 177 |
-
return
|
| 178 |
|
| 179 |
def clear_history():
|
| 180 |
classification_history.clear()
|
|
|
|
| 1 |
+
# email_classifier_v2025.10.29-01.py
|
| 2 |
+
|
| 3 |
import os
|
| 4 |
import gradio as gr
|
| 5 |
import requests
|
| 6 |
import json
|
| 7 |
from datetime import datetime
|
| 8 |
|
| 9 |
+
__version__ = "v2025.10.29-01"
|
| 10 |
+
|
| 11 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 12 |
GROQ_COMPLETION_URL = "https://api.groq.com/openai/v1/chat/completions"
|
| 13 |
GROQ_MODEL = "llama-3.1-8b-instant"
|
|
|
|
| 32 |
response = requests.post(GROQ_COMPLETION_URL, headers=headers, json=body)
|
| 33 |
response.raise_for_status()
|
| 34 |
return response.json()["choices"][0]["message"]["content"]
|
| 35 |
+
except Exception:
|
| 36 |
+
return "ERROR:model_request"
|
| 37 |
|
| 38 |
def email_classifier_router(raw_email):
|
| 39 |
prompt = (
|
| 40 |
+
"Your reply MUST BE valid compact JSON. NO TEXT OR EXPLANATION before or after the JSON. "
|
| 41 |
+
"Given the business email below, extract:"
|
| 42 |
"\n- Category (Support Request, Sales Inquiry, Finance/Billing, Urgent Incident, Spam/Marketing)"
|
| 43 |
"\n- Priority (Low, Medium, High)"
|
| 44 |
"\n- Suggested Recipient"
|
|
|
|
| 49 |
f"{raw_email}"
|
| 50 |
)
|
| 51 |
raw_result = groq_completion(prompt)
|
| 52 |
+
if raw_result == "ERROR:model_request":
|
| 53 |
+
return {
|
| 54 |
+
"Category": "ERROR",
|
| 55 |
+
"Priority": "ERROR",
|
| 56 |
+
"Suggested Recipient": "ERROR",
|
| 57 |
+
"Draft Response": "Model request failed, please check API or retry.",
|
| 58 |
+
"Summary": "ERROR",
|
| 59 |
+
"Action Items": ["ERROR"]
|
| 60 |
+
}
|
| 61 |
try:
|
| 62 |
output = json.loads(raw_result)
|
| 63 |
+
# Ensure keys present, fallback if missing
|
| 64 |
+
for key in ["Category", "Priority", "Suggested Recipient", "Draft Response", "Summary", "Action Items"]:
|
| 65 |
+
if key not in output:
|
| 66 |
+
output[key] = ""
|
| 67 |
+
except Exception:
|
| 68 |
output = {
|
| 69 |
+
"Category": "PARSE ERROR",
|
| 70 |
+
"Priority": "PARSE ERROR",
|
| 71 |
+
"Suggested Recipient": "",
|
| 72 |
+
"Draft Response": f"Could not extract valid JSON. Raw LLM output:\n{raw_result}",
|
| 73 |
+
"Summary": "PARSE ERROR",
|
| 74 |
+
"Action Items": ["PARSE ERROR"]
|
| 75 |
}
|
| 76 |
return output
|
| 77 |
|
|
|
|
| 89 |
css="""
|
| 90 |
body { background: linear-gradient(120deg, #10193a 0%, #175ad7 120%) !important; font-family: 'Inter', 'Segoe UI', Arial, sans-serif;}
|
| 91 |
.gradio-container { background: transparent !important; }
|
| 92 |
+
.artifact-header { background:linear-gradient(90deg,#283e9d 70%,#36d1fa 150%); border-radius:28px; padding:38px 78px; box-shadow:0 10px 52px #184fa988; margin:0 auto 38px auto; display:flex;align-items:center;justify-content:center;}
|
| 93 |
+
.artifact-header-text {font-size:3.1em;font-weight:900;color:#fff;text-shadow:0 4px 44px #09f6ef,0 2px 20px #2e96ff;}
|
| 94 |
.card-block { background: rgba(27,36,68,0.96); border-radius: 30px; box-shadow: 0 6px 42px #0b2269bb, 0 1.5px 0 #186fc055 inset; padding: 38px 28px 29px 28px; margin: 32px 0; color: #fafeff; border: 2px solid #2847a46c; backdrop-filter: blur(8px);}
|
| 95 |
input, textarea, .wrap.svelte-psy7la, .wrap.svelte-1ybjah6 { background: rgba(21,28,56,0.91)!important; border:1.5px solid #345ca3e3!important; color:#def1ff!important; border-radius: 14px !important; box-shadow: none !important;}
|
| 96 |
label, .label.svelte-1ybjah6 { color: #70acf8!important; font-size: 1em !important; font-weight: 400;}
|
|
|
|
| 112 |
"""
|
| 113 |
|
| 114 |
with gr.Blocks(css=css) as demo:
|
| 115 |
+
gr.HTML("""
|
| 116 |
+
<div class="artifact-header">
|
| 117 |
+
<span class="artifact-header-text">📧 AI Email Classifier & Router</span>
|
| 118 |
+
<span style='margin-left:30px; font-size:1.5em; color:#fff;opacity:.50;'>Artifact <b>{ver}</b></span>
|
| 119 |
+
</div>
|
| 120 |
+
""".format(ver=__version__))
|
| 121 |
+
|
| 122 |
with gr.Row():
|
| 123 |
with gr.Column(scale=6, min_width=420):
|
| 124 |
with gr.Group(elem_id="input-card", elem_classes=["card-block"]):
|
|
|
|
| 179 |
acts = result.get("Action Items", [])
|
| 180 |
acts = "\n".join(acts) if isinstance(acts, list) else acts
|
| 181 |
add_to_history(email_text, cat, pri, summ, dra)
|
| 182 |
+
badge = (
|
| 183 |
+
f"<div class='history-top'><span class='badge'>{cat}</span><span class='badge priority-{pri.lower()}'>{pri}</span></div>"
|
| 184 |
+
)
|
| 185 |
# Only show last 5 records
|
| 186 |
recent_history = classification_history[-5:][::-1]
|
| 187 |
history_html = "<div class='history-cardrow'>"
|
|
|
|
| 201 |
</div>
|
| 202 |
"""
|
| 203 |
history_html += "</div>"
|
| 204 |
+
return badge, rec, dra, summ, acts, history_html
|
| 205 |
|
| 206 |
def clear_history():
|
| 207 |
classification_history.clear()
|