"""Task definitions for the Customer Support environment. Each task includes a pool of realistic tickets and clear objectives. """ from __future__ import annotations from typing import Any, Dict, List from env.models import IssueCategory, Sentiment, Ticket # ── Ticket pools ────────────────────────────────────────────────────── _EASY_TICKETS: List[Dict[str, Any]] = [ { "ticket_id": "EASY-001", "customer_name": "Alice Johnson", "issue_text": ( "My order #9821 was supposed to arrive last Monday but I still " "haven't received it. The tracking page says 'in transit' for 5 days. " "Can you check what happened?" ), "category": IssueCategory.DELIVERY_DELAY, "sentiment": Sentiment.NEUTRAL, "order_value": 45.99, "priority": "normal", }, { "ticket_id": "EASY-002", "customer_name": "Bob Martinez", "issue_text": ( "I received the laptop stand today but the top plate is cracked and " "one of the screws is missing. The box was also dented. I need a " "replacement." ), "category": IssueCategory.DAMAGED_PRODUCT, "sentiment": Sentiment.ANGRY, "order_value": 89.50, "priority": "high", }, { "ticket_id": "EASY-003", "customer_name": "Caroline Pham", "issue_text": ( "I'd like a refund for order #3345. I returned the shoes two weeks " "ago and the tracking shows they were delivered back to your warehouse " "on March 28." ), "category": IssueCategory.REFUND_REQUEST, "sentiment": Sentiment.NEUTRAL, "order_value": 120.00, "priority": "normal", }, { "ticket_id": "EASY-004", "customer_name": "David Kim", "issue_text": ( "I was charged twice for order #7712. My credit card statement shows " "two charges of $54.99 on April 1st. Please fix this." ), "category": IssueCategory.BILLING_ERROR, "sentiment": Sentiment.ANGRY, "order_value": 54.99, "priority": "high", }, { "ticket_id": "EASY-005", "customer_name": "Emma Wilson", "issue_text": ( "Hi! I just wanted to know if the wireless headphones model WH-300 " "are compatible with my MacBook Pro M2. Thanks!" ), "category": IssueCategory.PRODUCT_INQUIRY, "sentiment": Sentiment.HAPPY, "order_value": 0.0, "priority": "low", }, ] _MEDIUM_TICKETS: List[Dict[str, Any]] = [ { "ticket_id": "MED-001", "customer_name": "Frank Torres", "issue_text": ( "THIS IS UNACCEPTABLE! I ordered a birthday gift for my daughter and " "it never arrived. The party was yesterday and she had nothing to " "open. I want a full refund AND compensation for this disaster. " "Order #5567, $199.99." ), "category": IssueCategory.DELIVERY_DELAY, "sentiment": Sentiment.ANGRY, "order_value": 199.99, "priority": "critical", }, { "ticket_id": "MED-002", "customer_name": "Grace Lee", "issue_text": ( "The ceramic vase I ordered arrived shattered. There was barely any " "bubble wrap in the box. This was supposed to be an anniversary gift. " "I'm very disappointed. Order #8834, paid $75." ), "category": IssueCategory.DAMAGED_PRODUCT, "sentiment": Sentiment.ANGRY, "order_value": 75.00, "priority": "high", }, { "ticket_id": "MED-003", "customer_name": "Henry Chen", "issue_text": ( "I returned three items from order #2291 but only received a partial " "refund of $30. The total should be $95.50. Can you look into this? " "I have the return receipts." ), "category": IssueCategory.REFUND_REQUEST, "sentiment": Sentiment.NEUTRAL, "order_value": 95.50, "priority": "normal", }, ] _HARD_TICKETS: List[Dict[str, Any]] = [ { "ticket_id": "HARD-001", "customer_name": "Isabella Rodriguez", "issue_text": ( "I'm FURIOUS! My $450 espresso machine arrived with a huge dent on " "the side, the water tank is cracked, AND I was charged twice! " "Order #1199. I've been a loyal customer for 5 years and this is " "how you treat me? I want a FULL refund, a replacement, AND someone " "from management to call me. If this isn't resolved today I'm " "disputing with my bank and posting on every review site." ), "category": IssueCategory.DAMAGED_PRODUCT, "sentiment": Sentiment.ANGRY, "order_value": 450.00, "priority": "critical", }, { "ticket_id": "HARD-002", "customer_name": "James O'Brien", "issue_text": ( "Multiple issues: (1) Order #4421 — gaming keyboard — keys are " "sticky out of the box. (2) Order #4422 — mouse pad — wrong item " "sent, I got a phone case instead. (3) My account shows a pending " "charge of $200 that I didn't authorize. I need ALL of these fixed. " "Total value across orders is $310." ), "category": IssueCategory.BILLING_ERROR, "sentiment": Sentiment.ANGRY, "order_value": 310.00, "priority": "critical", }, { "ticket_id": "HARD-003", "customer_name": "Karen Nakamura", "issue_text": ( "I placed a bulk order of 50 custom T-shirts for a company event " "(order #6600, $1,250). Half of them have the wrong logo printed " "and the rest are the wrong sizes. The event is in 3 days. I need " "either a rush reprint or a full refund plus compensation for the " "expedited order I'll have to place elsewhere. This has put my job " "at risk." ), "category": IssueCategory.DAMAGED_PRODUCT, "sentiment": Sentiment.ANGRY, "order_value": 1250.00, "priority": "critical", }, ] # ── Task definitions ────────────────────────────────────────────────── TASKS = { "easy_classification": { "id": "easy_classification", "name": "Ticket Classification", "difficulty": "easy", "description": ( "Classify the customer support ticket into the correct issue " "category. The agent must submit a single 'classify' action with " "the right category label." ), "objective": ( "Read the ticket and classify it into one of: delivery_delay, " "damaged_product, refund_request, billing_error, product_inquiry, " "account_issue." ), "required_actions": ["classify"], "max_steps": 3, "tickets": _EASY_TICKETS, }, "medium_response": { "id": "medium_response", "name": "Response Generation", "difficulty": "medium", "description": ( "Classify the ticket AND generate an empathetic, helpful response " "to the customer. Graded on classification accuracy and response " "quality (empathy, specificity, professionalism)." ), "objective": ( "1. Classify the ticket correctly. " "2. Generate a professional, empathetic response that acknowledges " "the customer's issue and outlines next steps." ), "required_actions": ["classify", "respond"], "max_steps": 5, "tickets": _MEDIUM_TICKETS, }, "hard_resolution": { "id": "hard_resolution", "name": "Full Resolution Workflow", "difficulty": "hard", "description": ( "Execute the full customer support workflow: classify the ticket, " "generate a response, and take the appropriate resolution action " "(refund, escalate, or resolve). Graded on all dimensions." ), "objective": ( "1. Classify the ticket. " "2. Generate a professional response. " "3. Take the correct resolution action (refund / escalate / resolve) " "based on the ticket's severity, order value, and customer sentiment." ), "required_actions": ["classify", "respond", "refund|escalate|resolve"], "max_steps": 10, "tickets": _HARD_TICKETS, }, } def get_task(task_id: str) -> dict: """Return task definition by ID.""" if task_id not in TASKS: raise ValueError( f"Unknown task '{task_id}'. Available: {list(TASKS.keys())}" ) return TASKS[task_id] def get_ticket_for_task(task_id: str, ticket_index: int = 0) -> Ticket: """Return a Ticket model for the given task and index.""" task = get_task(task_id) tickets = task["tickets"] idx = ticket_index % len(tickets) return Ticket(**tickets[idx])