""" agent.py — Groq API version Uses Groq's free API with llama-3.3-70b-versatile model. Groq is extremely fast (200+ tokens/sec) and free to use. Setup: 1. Go to console.groq.com → sign up → API Keys → Create Key 2. Set: export GROQ_API_KEY=gsk_your_key_here (Mac/Linux) set GROQ_API_KEY=gsk_your_key_here (Windows) 3. pip install groq """ import os import json import re from groq import Groq client = Groq(api_key=os.environ.get("GROQ_API_KEY", "")) MODEL = "llama-3.3-70b-versatile" # Free, fast, high quality def _call(system: str, user: str, max_tokens: int = 800) -> str: """Call Groq API and return text response.""" response = client.chat.completions.create( model=MODEL, max_tokens=max_tokens, temperature=0.2, messages=[ {"role": "system", "content": system}, {"role": "user", "content": user}, ], ) return response.choices[0].message.content.strip() # ── CLASSIFICATION ──────────────────────────────────────────────────────────── CLASSIFY_SYSTEM = """You are a support ticket classifier for a multi-domain helpdesk. Classify the ticket into exactly these fields and respond ONLY with valid JSON. Fields: - domain: one of ["hackerrank", "claude", "visa", "unknown"] * hackerrank → coding assessments, candidates, recruiters, HackerRank platform, proctoring, plagiarism * claude → Claude AI assistant, claude.ai, Anthropic API, AI conversations, Claude subscription * visa → Visa card, ATM, transactions, payments, card dispute, merchant * unknown → unclear or does not match any domain - request_type: one of ["faq", "billing", "bug_report", "account_access", "assessment", "feature_request", "fraud", "complaint", "how_to", "permissions", "other"] - product_area: short phrase (e.g. "password reset", "card dispute", "API usage", "candidate results") - confidence: "high" | "medium" | "low" Respond ONLY with a JSON object. No markdown fences. No explanation. No extra text.""" def classify_ticket(ticket_text: str) -> dict: """Classify a support ticket. Returns dict.""" raw = _call(CLASSIFY_SYSTEM, f"Classify this ticket:\n\n{ticket_text}", max_tokens=200) try: clean = raw.strip().strip("```json").strip("```").strip() return json.loads(clean) except json.JSONDecodeError: # Try to extract JSON object with regex match = re.search(r'\{.*?\}', clean, re.DOTALL) if match: try: return json.loads(match.group(0)) except Exception: pass return _rule_based_classify(ticket_text) def _rule_based_classify(text: str) -> dict: """Fast keyword fallback if LLM JSON parse fails.""" t = text.lower() if any(w in t for w in ["hackerrank", "assessment", "candidate", "recruiter", "plagiar", "proctoring"]): domain = "hackerrank" elif any(w in t for w in ["claude", "anthropic", "claude.ai", "ai assistant"]): domain = "claude" elif any(w in t for w in ["visa", "card", "atm", "transaction", "merchant", "chargeback"]): domain = "visa" else: domain = "unknown" if any(w in t for w in ["fraud", "unauthorized", "stolen", "scam"]): rtype = "fraud" elif any(w in t for w in ["charge", "refund", "billing", "invoice"]): rtype = "billing" elif any(w in t for w in ["password", "login", "locked", "hacked", "access"]): rtype = "account_access" elif any(w in t for w in ["bug", "error", "broken", "crash", "not working"]): rtype = "bug_report" else: rtype = "faq" return {"domain": domain, "request_type": rtype, "product_area": f"{domain} support", "confidence": "medium"} # ── RESPONSE GENERATION ─────────────────────────────────────────────────────── RESPONSE_SYSTEM = """You are a professional, friendly support agent. STRICT RULE: Answer ONLY using the provided support documentation. Do NOT invent any policies, prices, steps, or features not in the docs. If docs don't cover something, say so honestly and suggest contacting support directly. Keep responses concise: 3-5 sentences for simple FAQs, up to 8 for complex issues.""" def generate_response(ticket_text: str, docs: list[dict]) -> str: """Generate a grounded support response using retrieved docs.""" if not docs: return ( "Thank you for reaching out. I wasn't able to find relevant documentation " "for your specific query in our support corpus. Please contact our support " "team directly for personalized assistance." ) context = "\n\n---\n\n".join( f"[Source {i}: {d['title']}]\n{d.get('snippet', d['text'][:800])}" for i, d in enumerate(docs[:4], 1) ) user_prompt = f"""Support Documentation: {context} --- Customer Issue: {ticket_text} Write a helpful, accurate response using only the documentation above.""" return _call(RESPONSE_SYSTEM, user_prompt, max_tokens=500) # ── ESCALATION MESSAGE ──────────────────────────────────────────────────────── ESCALATE_SYSTEM = """You are a support agent writing an escalation acknowledgment. Write 2-3 sentences: acknowledge the customer's concern, say a specialist will review it, and set a reasonable expectation. Be warm and professional. Do NOT attempt to resolve the issue. Do NOT reveal internal routing decisions.""" def generate_escalation_message(ticket_text: str, escalation_reason: str) -> str: """Generate a customer-facing escalation message.""" return _call( ESCALATE_SYSTEM, f"Customer issue: {ticket_text}\n\n(Internal note - do not reveal: {escalation_reason})", max_tokens=180, )