import os import gradio as gr import requests import json from datetime import datetime GROQ_API_KEY = os.getenv("GROQ_API_KEY") GROQ_COMPLETION_URL = "https://api.groq.com/openai/v1/chat/completions" GROQ_MODEL = "llama-3.1-8b-instant" classification_history = [] def groq_completion(prompt, sys_prompt=None): headers = {"Authorization": f"Bearer {GROQ_API_KEY}", "Content-Type": "application/json"} body = { "model": GROQ_MODEL, "messages": [ {"role": "system", "content": sys_prompt or "You are a fast and reliable business email assistant."}, {"role": "user", "content": prompt} ], "temperature": 0.3, "max_tokens": 512 } try: response = requests.post(GROQ_COMPLETION_URL, headers=headers, json=body) response.raise_for_status() return response.json()["choices"][0]["message"]["content"] except Exception: return "ERROR:model_request" def email_classifier_router(raw_email): prompt = ( "Your reply MUST BE valid compact JSON. NO TEXT OR EXPLANATION before or after the JSON. " "Given the business email below, extract:" "\n- Category (Support Request, Sales Inquiry, Finance/Billing, Urgent Incident, Spam/Marketing)" "\n- Priority (Low, Medium, High)" "\n- Suggested Recipient" "\n- Draft Response (professional reply text)" "\n- Summary (one sentence)" "\n- Action Items (numbered list)\n" "Format: {\"Category\":..., \"Priority\":..., \"Suggested Recipient\":..., \"Draft Response\":..., \"Summary\":..., \"Action Items\":[...]}\n\n" f"{raw_email}" ) raw_result = groq_completion(prompt) if raw_result == "ERROR:model_request": return { "Category": "ERROR", "Priority": "ERROR", "Suggested Recipient": "ERROR", "Draft Response": "Model request failed, please check API or retry.", "Summary": "ERROR", "Action Items": ["ERROR"] } try: output = json.loads(raw_result) for key in ["Category", "Priority", "Suggested Recipient", "Draft Response", "Summary", "Action Items"]: if key not in output: output[key] = "" except Exception: output = { "Category": "PARSE ERROR", "Priority": "PARSE ERROR", "Suggested Recipient": "", "Draft Response": f"Could not extract valid JSON. Raw LLM output:\n{raw_result}", "Summary": "PARSE ERROR", "Action Items": ["PARSE ERROR"] } return output def add_to_history(email, cat, pri, summ, dra): entry = { "datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "email": email, "category": cat, "priority": pri, "summary": summ, "draft": dra } classification_history.append(entry) css = """ body { background: linear-gradient(120deg,#10193a 0%,#175ad7 120%)!important; font-family:'Inter','Segoe UI',Arial,sans-serif;} .gradio-container { background: transparent !important; } .main-pill-header { display: flex; flex-direction: column; align-items: center; justify-content: center; margin: 28px auto 23px auto; width: fit-content; padding: 0; } .main-pill { background:linear-gradient(90deg,#3084f4 0%,#39d5ff 97%); border-radius:34px; padding:33px 66px 30px 60px; box-shadow:0 0 90px 20px #42e5ff3a,0 0 0 12px #11b6ff0a; display:flex;flex-direction:column;align-items:center; border:3.5px solid #79e8ffcc; } .main-pill-title { font-size:3.2em;font-weight:900;color:#fff;letter-spacing:.04em; text-shadow:0 10px 70px #1ad1f3a9,0 3px 24px #36b0ecba; } .main-pill-subtitle { color:#e7f7ff;margin-top:11px;font-size:1.17em;font-weight:500;letter-spacing:.01em;text-shadow:0 2px 14px #99faffad; } .section-header { font-size:1.55em; font-weight:800; letter-spacing:.02em; color:#fff; margin-top:28px; margin-bottom:0px !important; /* No gap below header */ text-shadow:0 4px 34px #09dede,0 2px 8px #2989d2; display:block; } .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:0 0 32px 0 !important; color:#fafeff; border:2px solid #2847a46c; backdrop-filter:blur(8px); } .card-gap { height: 5px; } .result-labels-row{display:flex;align-items:center;gap:18px;margin-bottom:10px;margin-top:-6px;} .result-label-text{font-size:1.08em;font-weight:700;color:#daf6ff;letter-spacing:.01em;margin-right:2px;} .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:8px;font-size:1.02em;vertical-align:middle;} .priority-high{background:linear-gradient(90deg,#ed4864 60%,#fa4764 150%)!important;color:#fff;} .priority-medium{background:linear-gradient(90deg,#f3aa36 70%,#f6ea48 120%)!important;color:#222;} .priority-low{background:linear-gradient(90deg,#18cd67 60%,#27f8ac 150%)!important;color:#fff;} .history-cardrow {display:flex;flex-wrap:wrap;gap:30px;} .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 30px 16px 30px;min-width:340px;max-width:380px;box-sizing:border-box;} .history-top { display:flex; align-items:center; justify-content:space-between;} .history-catlab { font-size:1em;font-weight:600;margin-top:3px; } .history-summ { font-style:italic;font-size:1em;margin-top:10px;color:#eaf7ff;} .history-btnrow { display:flex; gap:12px; margin-top:16px;} .btn-md {padding:10px 22px;border-radius:14px; border:none;font-size:1.03em;font-weight:700;background:#3246b8; color:#fff;} .btn-md:hover { background:#2284cc;} .clear-btn { background:#db2828!important;color:#fff!important;padding:10px 24px;font-weight:600;border-radius:13px;border:none;font-size:1.07em;margin-bottom:22px;} #paste-btn .wrap {font-size:1.13em;} """ with gr.Blocks(css=css) as demo: gr.HTML("""