Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -44,15 +44,12 @@ def email_classifier_router(raw_email):
|
|
| 44 |
"- Draft professional reply\n"
|
| 45 |
"- Summary (one sentence)\n"
|
| 46 |
"- Action Items (numbered list)\n\n"
|
| 47 |
-
"Format:\n"
|
| 48 |
-
'{"Category":"...", "Priority":"...", "Suggested Recipient":"...", "Draft Response":"...", "Summary":"...", "Action Items":["...","..."]}\n\n'
|
| 49 |
f"{raw_email}"
|
| 50 |
)
|
| 51 |
raw_result = groq_completion(prompt)
|
| 52 |
try:
|
| 53 |
output = json.loads(raw_result)
|
| 54 |
except Exception as e:
|
| 55 |
-
print(f"JSON parse error: {e}\nRAW: {raw_result}")
|
| 56 |
output = {
|
| 57 |
"Category": "Error",
|
| 58 |
"Priority": "Error",
|
|
@@ -73,91 +70,56 @@ def add_to_history(email, cat, pri, summ):
|
|
| 73 |
}
|
| 74 |
classification_history.append(entry)
|
| 75 |
|
| 76 |
-
css
|
| 77 |
body { background: linear-gradient(120deg, #10193a 0%, #175ad7 120%) !important; font-family: 'Inter', 'Segoe UI', Arial, sans-serif;}
|
| 78 |
.gradio-container { background: transparent !important; }
|
| 79 |
-
.card-block {
|
| 80 |
-
|
| 81 |
-
border-radius: 30px;
|
| 82 |
-
box-shadow: 0 6px 42px #0b2269bb, 0 1.5px 0 #186fc055 inset;
|
| 83 |
-
padding: 38px 28px 29px 28px;
|
| 84 |
-
margin: 32px 0;
|
| 85 |
-
color: #fafeff;
|
| 86 |
-
border: 2px solid #2847a46c;
|
| 87 |
-
backdrop-filter: blur(8px);
|
| 88 |
-
}
|
| 89 |
-
input, textarea, .wrap.svelte-psy7la, .wrap.svelte-1ybjah6 {
|
| 90 |
-
background: rgba(21,28,56,0.91)!important; border:1.5px solid #345ca3e3!important; color:#def1ff!important;
|
| 91 |
-
border-radius: 14px !important; box-shadow: none !important;
|
| 92 |
-
}
|
| 93 |
label, .label.svelte-1ybjah6 { color: #70acf8!important; font-size: 1em !important; font-weight: 400;}
|
| 94 |
-
.glass-btn {
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
background: linear-gradient(90deg,#336bc0 60%,#1ae6ff 140%)!important;
|
| 98 |
-
color: #fff !important;
|
| 99 |
-
letter-spacing: .04em;
|
| 100 |
-
border: none !important;
|
| 101 |
-
font-size: 1.11em !important;
|
| 102 |
-
font-weight: 600;
|
| 103 |
-
padding: 12px 0 10px 0 !important;
|
| 104 |
-
}
|
| 105 |
-
.card-header {
|
| 106 |
-
font-size: 1.32em;
|
| 107 |
-
font-weight: 800;
|
| 108 |
-
letter-spacing: 0.01em;
|
| 109 |
-
margin: 0 0 15px 0;
|
| 110 |
-
color: #ffffff;
|
| 111 |
-
text-shadow: 0 3px 24px #175ad799;
|
| 112 |
-
display: flex; align-items: center;
|
| 113 |
-
}
|
| 114 |
-
.badge {
|
| 115 |
-
display:inline-block;
|
| 116 |
-
border-radius:999px;
|
| 117 |
-
padding:4px 16px;
|
| 118 |
-
font-weight:600;
|
| 119 |
-
background:linear-gradient(90deg,#2870ee 70%,#13e4fc 140%);
|
| 120 |
-
box-shadow:0 1px 12px #0663fd29;
|
| 121 |
-
color:#fff;
|
| 122 |
-
margin-right:12px;
|
| 123 |
-
font-size:1.05em;
|
| 124 |
-
vertical-align:middle;
|
| 125 |
-
}
|
| 126 |
.priority-high { background:linear-gradient(90deg,#ed4864 60%,#fa4764 150%)!important; color:#fff;}
|
| 127 |
.priority-medium { background:linear-gradient(90deg,#f3aa36 70%,#f6ea48 120%)!important;color:#222;}
|
| 128 |
.priority-low { background:linear-gradient(90deg,#18cd67 60%,#27f8ac 150%)!important;color:#fff;}
|
| 129 |
-
.history-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
font-size: 1.01em;
|
| 139 |
-
}
|
| 140 |
-
.history-record.selected { border-left:7px solid #22eef8; background: rgba(44,86,166,1);}
|
| 141 |
-
.history-record .small { color:#a6c5e7; font-size: .92em;}
|
| 142 |
"""
|
| 143 |
|
| 144 |
with gr.Blocks(css=css) as demo:
|
| 145 |
-
gr.Markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
with gr.Row():
|
| 147 |
-
with gr.Column(scale=
|
| 148 |
with gr.Group(elem_id="input-card", elem_classes=["card-block"]):
|
| 149 |
gr.Markdown("<div class='card-header'>✉️ Paste Your Email</div>")
|
| 150 |
-
gr.Markdown("<span style='font-size
|
| 151 |
with gr.Row():
|
| 152 |
support_button = gr.Button("Support Request")
|
| 153 |
sales_button = gr.Button("Sales Inquiry")
|
| 154 |
finance_button = gr.Button("Finance / Billing")
|
| 155 |
urgent_button = gr.Button("Urgent Incident")
|
| 156 |
spam_button = gr.Button("Spam / Marketing")
|
|
|
|
|
|
|
|
|
|
| 157 |
email_box = gr.Textbox(lines=8, label="", placeholder="From: ...\nSubject: ...")
|
| 158 |
-
classify_btn = gr.Button("Classify Email", elem_classes=["glass-btn"])
|
| 159 |
|
| 160 |
-
with gr.Column(scale=
|
| 161 |
with gr.Group(elem_id="output-card", elem_classes=["card-block"]):
|
| 162 |
gr.Markdown("""<div class='card-header'>🧮 Classification Results</div>""")
|
| 163 |
result_labels = gr.Markdown("")
|
|
@@ -167,20 +129,21 @@ with gr.Blocks(css=css) as demo:
|
|
| 167 |
action_items = gr.Textbox(lines=3, label="Extracted Action Items")
|
| 168 |
|
| 169 |
with gr.Group(elem_id="history-card", elem_classes=["card-block"]):
|
| 170 |
-
gr.Markdown("""<div class='card-header'>🕒 Classification History</div>""")
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
|
|
|
| 184 |
|
| 185 |
def classify_and_render(email_text):
|
| 186 |
result = email_classifier_router(email_text)
|
|
@@ -193,26 +156,27 @@ with gr.Blocks(css=css) as demo:
|
|
| 193 |
acts = "\n".join(acts) if isinstance(acts, list) else acts
|
| 194 |
add_to_history(email_text, cat, pri, summ)
|
| 195 |
badge = f"<b>Category:</b> <span class='badge'>{cat}</span> <b>Priority:</b> <span class='badge priority-{pri.lower()}'>{pri}</span>"
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
|
|
|
|
|
|
| 209 |
|
| 210 |
classify_btn.click(
|
| 211 |
classify_and_render,
|
| 212 |
inputs=[email_box],
|
| 213 |
-
outputs=[result_labels, recipient, draft_response, summary, action_items,
|
| 214 |
)
|
| 215 |
-
load_btn.click(load_history, inputs=[history_radio], outputs=email_box)
|
| 216 |
|
| 217 |
if __name__ == "__main__":
|
| 218 |
demo.launch()
|
|
|
|
| 44 |
"- Draft professional reply\n"
|
| 45 |
"- Summary (one sentence)\n"
|
| 46 |
"- Action Items (numbered list)\n\n"
|
|
|
|
|
|
|
| 47 |
f"{raw_email}"
|
| 48 |
)
|
| 49 |
raw_result = groq_completion(prompt)
|
| 50 |
try:
|
| 51 |
output = json.loads(raw_result)
|
| 52 |
except Exception as e:
|
|
|
|
| 53 |
output = {
|
| 54 |
"Category": "Error",
|
| 55 |
"Priority": "Error",
|
|
|
|
| 70 |
}
|
| 71 |
classification_history.append(entry)
|
| 72 |
|
| 73 |
+
css="""
|
| 74 |
body { background: linear-gradient(120deg, #10193a 0%, #175ad7 120%) !important; font-family: 'Inter', 'Segoe UI', Arial, sans-serif;}
|
| 75 |
.gradio-container { background: transparent !important; }
|
| 76 |
+
.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);}
|
| 77 |
+
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;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
label, .label.svelte-1ybjah6 { color: #70acf8!important; font-size: 1em !important; font-weight: 400;}
|
| 79 |
+
.glass-btn { border-radius: 18px !important; box-shadow: 0 4px 18px #287bef25 !important; background: linear-gradient(90deg,#336bc0 60%,#1ae6ff 140%)!important; color: #fff !important; letter-spacing: .04em; border: none !important; font-size: 1.11em !important; font-weight: 600; padding: 12px 0 10px 0 !important;}
|
| 80 |
+
.card-header { font-size: 2.1em;font-weight:900;letter-spacing:0.01em; margin:0 0 28px 0; color: #fff; text-shadow: 0 5px 36px #175ad799;}
|
| 81 |
+
.badge { display:inline-block; border-radius:999px; padding:4px 16px; font-weight:600; background:linear-gradient(90deg,#2870ee 70%,#13e4fc 140%); box-shadow:0 1px 12px #0663fd29; color:#fff; margin-right:12px; font-size:1.12em; vertical-align:middle;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
.priority-high { background:linear-gradient(90deg,#ed4864 60%,#fa4764 150%)!important; color:#fff;}
|
| 83 |
.priority-medium { background:linear-gradient(90deg,#f3aa36 70%,#f6ea48 120%)!important;color:#222;}
|
| 84 |
.priority-low { background:linear-gradient(90deg,#18cd67 60%,#27f8ac 150%)!important;color:#fff;}
|
| 85 |
+
.history-cardbox {display:flex;gap:28px;flex-wrap:wrap;}
|
| 86 |
+
.history-record {background: rgba(35, 54, 90, 0.92);color: #e2f5ff;margin:0 0 13px 0; border-radius: 16px;border-left: 5px solid #10baff; box-shadow: 0 2px 12px #08526733;padding:20px 26px 16px 26px;min-width:340px;max-width:370px;box-sizing:border-box;}
|
| 87 |
+
.history-ts { color:#a7b7ce;font-size:0.96em; }
|
| 88 |
+
.history-catlab { font-size:1.1em;font-weight:600; }
|
| 89 |
+
.history-summ { font-style:italic;font-size:1.08em;margin-top:7px;color:#eaf7ff;}
|
| 90 |
+
.history-btnrow { display:flex; gap:12px; margin-top:14px;}
|
| 91 |
+
.btn-md {padding:10px 22px;border-radius:14px; border:none;font-size:1.03em;font-weight:700;background:#3246b8; color:#fff;}
|
| 92 |
+
.btn-md:hover { background:#2284cc;}
|
| 93 |
+
.card-header svg {width:1.5em;height:1.5em;vertical-align:middle; margin-right:8px;}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
"""
|
| 95 |
|
| 96 |
with gr.Blocks(css=css) as demo:
|
| 97 |
+
gr.Markdown("""
|
| 98 |
+
<div style='text-align:center; margin:10px 0 22px 0; font-size:2.5em; font-weight:900; letter-spacing:.01em; color:#fff;'>
|
| 99 |
+
<svg width="36" height="36" fill="none" stroke="#26e2ec" stroke-width="3" style="vertical-align:middle;margin-right:9px;">
|
| 100 |
+
<circle cx="18" cy="18" r="16" fill="#0133a0" stroke-width="0"/>
|
| 101 |
+
<text x="50%" y="55%" text-anchor="middle" dominant-baseline="middle" font-family="Segoe UI" font-size="19" fill="#fff">✉️</text>
|
| 102 |
+
</svg>
|
| 103 |
+
AI Email Classifier & Router
|
| 104 |
+
</div>
|
| 105 |
+
""")
|
| 106 |
with gr.Row():
|
| 107 |
+
with gr.Column(scale=6, min_width=420):
|
| 108 |
with gr.Group(elem_id="input-card", elem_classes=["card-block"]):
|
| 109 |
gr.Markdown("<div class='card-header'>✉️ Paste Your Email</div>")
|
| 110 |
+
gr.Markdown("<span style='font-size:1.05em; color:#a6c5e7;'>Choose Example:</span>")
|
| 111 |
with gr.Row():
|
| 112 |
support_button = gr.Button("Support Request")
|
| 113 |
sales_button = gr.Button("Sales Inquiry")
|
| 114 |
finance_button = gr.Button("Finance / Billing")
|
| 115 |
urgent_button = gr.Button("Urgent Incident")
|
| 116 |
spam_button = gr.Button("Spam / Marketing")
|
| 117 |
+
gr.Button("Account issue")
|
| 118 |
+
gr.Button("Legal inquiry")
|
| 119 |
+
gr.Button("Meeting scheduling")
|
| 120 |
email_box = gr.Textbox(lines=8, label="", placeholder="From: ...\nSubject: ...")
|
|
|
|
| 121 |
|
| 122 |
+
with gr.Column(scale=8, min_width=430):
|
| 123 |
with gr.Group(elem_id="output-card", elem_classes=["card-block"]):
|
| 124 |
gr.Markdown("""<div class='card-header'>🧮 Classification Results</div>""")
|
| 125 |
result_labels = gr.Markdown("")
|
|
|
|
| 129 |
action_items = gr.Textbox(lines=3, label="Extracted Action Items")
|
| 130 |
|
| 131 |
with gr.Group(elem_id="history-card", elem_classes=["card-block"]):
|
| 132 |
+
gr.Markdown("""<div class='card-header' style="margin-bottom:20px;">🕒 Classification History</div>""")
|
| 133 |
+
history_cardbox = gr.HTML(elem_id="history-cardbox")
|
| 134 |
+
|
| 135 |
+
exs = [
|
| 136 |
+
"From: support@mycrm.com\nSubject: Cannot login to dashboard\nBody: I've been unable to sign into my account for 3 days even after password reset.",
|
| 137 |
+
"From: prospect@b2bsales.com\nSubject: Inquiry about pricing\nHi, Can you send over your enterprise pricing and product details for Q1?",
|
| 138 |
+
"From: accounts@acmecorp.com\nSubject: Invoice #00456 Payment Reminder\nBody: This is a reminder that your invoice #00456 for $4,200.00 is due next week.",
|
| 139 |
+
"From: jane.doe@company.org\nSubject: Meeting request\nCould you schedule a call with the marketing team for Friday 3pm?",
|
| 140 |
+
"From: admin@infra.com\nSubject: URGENT Server Down\nThe production DB went down at 11:22 UTC, please escalate to engineering immediately.",
|
| 141 |
+
"From: legal@mycrm.com\nSubject: Contract question\nCan your team confirm paragraph 7.4 in the service agreement?",
|
| 142 |
+
"From: billing@b2bsales.com\nSubject: Account update\nPlease update billing address for account #AC10239.",
|
| 143 |
+
"From: teamlead@company.org\nSubject: Follow-up on project\nHi, just wanted a final status update on our July roadmap deliverables.",
|
| 144 |
+
]
|
| 145 |
+
for idx, ex in enumerate(exs):
|
| 146 |
+
demo.children[0].children[0].children[1].children[idx].click(lambda ex=ex: ex, None, email_box)
|
| 147 |
|
| 148 |
def classify_and_render(email_text):
|
| 149 |
result = email_classifier_router(email_text)
|
|
|
|
| 156 |
acts = "\n".join(acts) if isinstance(acts, list) else acts
|
| 157 |
add_to_history(email_text, cat, pri, summ)
|
| 158 |
badge = f"<b>Category:</b> <span class='badge'>{cat}</span> <b>Priority:</b> <span class='badge priority-{pri.lower()}'>{pri}</span>"
|
| 159 |
+
# Native HTML styled history cards
|
| 160 |
+
history_html = ""
|
| 161 |
+
for idx, h in enumerate(classification_history[::-1]):
|
| 162 |
+
history_html += f"""
|
| 163 |
+
<div class='history-record'>
|
| 164 |
+
<div class='history-ts'>{h['datetime']}</div>
|
| 165 |
+
<div class='history-catlab'>Category: <span class='badge'>{h['category']}</span> Priority: <span class='badge priority-{h['priority'].lower()}'>{h['priority']}</span></div>
|
| 166 |
+
<div class='history-summ'>{h['summary']}</div>
|
| 167 |
+
<div class='history-btnrow'>
|
| 168 |
+
<button class='btn-md' onclick="navigator.clipboard.writeText({json.dumps(h['email'])});">Copy Email</button>
|
| 169 |
+
<button class='btn-md' onclick="window.parent.postMessage({json.dumps(h['email'])}, '*');">Load</button>
|
| 170 |
+
</div>
|
| 171 |
+
</div>
|
| 172 |
+
"""
|
| 173 |
+
return badge, rec, dra, summ, acts, history_html
|
| 174 |
|
| 175 |
classify_btn.click(
|
| 176 |
classify_and_render,
|
| 177 |
inputs=[email_box],
|
| 178 |
+
outputs=[result_labels, recipient, draft_response, summary, action_items, history_cardbox]
|
| 179 |
)
|
|
|
|
| 180 |
|
| 181 |
if __name__ == "__main__":
|
| 182 |
demo.launch()
|