File size: 15,310 Bytes
81aa69d
 
 
 
 
 
 
 
 
6c741e5
81aa69d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6c741e5
81aa69d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
"""
Task definitions for the Customer Support Environment.

Contains 3 tasks with increasing difficulty:
  1. EASY   β€” Simple FAQ resolution
  2. MEDIUM β€” Conditional refund processing
  3. HARD   β€” Complex complaint escalation with angry customer
"""

from typing import Any, Dict, List, Optional

from models import (
    CustomerMessage,
    CustomerSentiment,
    Difficulty,
    TicketCategory,
    TicketInfo,
    TicketPriority,
    TicketStatus,
)


# ──────────────────────────────────────────────────────────────────
# Company Policies (shared context)
# ──────────────────────────────────────────────────────────────────

COMPANY_POLICIES = {
    "refund_policy": (
        "Refund Policy: Full refunds are available within 30 days of purchase for "
        "unopened items. Opened items can receive a 50% refund within 15 days. "
        "Digital products are non-refundable after download. Defective items receive "
        "a full refund or replacement at any time with proof of defect. "
        "Shipping costs are non-refundable unless the return is due to our error."
    ),
    "shipping_policy": (
        "Shipping Policy: Standard shipping takes 5–7 business days. Express shipping "
        "takes 2–3 business days. Free shipping on orders over $50. Tracking numbers "
        "are sent via email within 24 hours of shipment. International orders may "
        "take 10–15 business days."
    ),
    "return_policy": (
        "Return Policy: Items must be in original packaging. Return shipping labels "
        "are provided for defective items. Customer pays return shipping for change-of-mind "
        "returns. Refunds are processed within 5–7 business days after receiving the return."
    ),
    "escalation_policy": (
        "Escalation Policy: If a customer expresses extreme dissatisfaction or the "
        "issue cannot be resolved in standard steps, offer to escalate to a senior "
        "support specialist. Always acknowledge the customer's frustration and offer "
        "a concrete next step. Compensation (store credit or discount) may be offered "
        "for significant inconvenience, up to 15% of order value."
    ),
}


# ──────────────────────────────────────────────────────────────────
# Helper: build a task definition dict
# ──────────────────────────────────────────────────────────────────

def _task(
    task_id: str,
    difficulty: Difficulty,
    ticket: Dict[str, Any],
    initial_message: str,
    policy_keys: List[str],
    max_steps: int,
    expected_keywords: List[str],
    grading_rubric: Dict[str, Any],
    follow_up_messages: Optional[List[str]] = None,
) -> Dict[str, Any]:
    return {
        "task_id": task_id,
        "difficulty": difficulty,
        "ticket": ticket,
        "initial_message": initial_message,
        "follow_up_messages": follow_up_messages or [],
        "policy_context": "\n\n".join(COMPANY_POLICIES[k] for k in policy_keys),
        "max_steps": max_steps,
        "expected_keywords": expected_keywords,
        "grading_rubric": grading_rubric,
    }


# ──────────────────────────────────────────────────────────────────
# TASK 1 β€” EASY: Simple FAQ
# ──────────────────────────────────────────────────────────────────

TASK_EASY_FAQ = _task(
    task_id="easy_faq",
    difficulty=Difficulty.EASY,
    ticket={
        "ticket_id": "TKT-1001",
        "category": TicketCategory.FAQ,
        "priority": TicketPriority.LOW,
        "status": TicketStatus.OPEN,
        "customer_name": "Sarah Johnson",
        "customer_sentiment": CustomerSentiment.NEUTRAL,
        "subject": "Where is my order?",
        "order_id": "ORD-55821",
        "product_name": "Wireless Bluetooth Headphones",
        "purchase_date": "2026-03-28",
        "purchase_amount": 79.99,
    },
    initial_message=(
        "Hi, I placed an order about a week ago for Wireless Bluetooth Headphones "
        "(order #ORD-55821) and I haven't received any shipping update yet. "
        "Can you tell me where my order is?"
    ),
    policy_keys=["shipping_policy"],
    max_steps=3,
    expected_keywords=[
        "tracking", "shipping", "5-7 business days", "5–7 business days",
        "email", "order", "ORD-55821",
    ],
    grading_rubric={
        "correctness": {
            "weight": 0.3,
            "criteria": [
                {"keyword_group": ["tracking", "track"], "points": 0.4, "desc": "Mentions tracking info"},
                {"keyword_group": ["5-7", "5–7", "business days", "5 to 7"], "points": 0.3, "desc": "Mentions shipping timeframe"},
                {"keyword_group": ["email", "notification", "update"], "points": 0.3, "desc": "Mentions how they'll receive updates"},
            ],
        },
        "tone": {
            "weight": 0.3,
            "criteria": {
                "positive_signals": ["thank", "appreciate", "glad to help", "happy to", "pleased", "welcome"],
                "negative_signals": ["not my problem", "deal with it", "figure it out", "whatever", "stupid"],
            },
        },
        "completeness": {
            "weight": 0.4,
            "criteria": [
                {"check": "addresses_question", "points": 0.4, "desc": "Directly answers the question"},
                {"check": "provides_next_steps", "points": 0.3, "desc": "Offers next steps or actions"},
                {"check": "references_order", "points": 0.3, "desc": "References the specific order"},
            ],
        },
    },
)

# ──────────────────────────────────────────────────────────────────
# TASK 2 β€” MEDIUM: Refund Request with Conditions
# ──────────────────────────────────────────────────────────────────

TASK_MEDIUM_REFUND = _task(
    task_id="medium_refund",
    difficulty=Difficulty.MEDIUM,
    ticket={
        "ticket_id": "TKT-2047",
        "category": TicketCategory.REFUND,
        "priority": TicketPriority.MEDIUM,
        "status": TicketStatus.OPEN,
        "customer_name": "Michael Chen",
        "customer_sentiment": CustomerSentiment.FRUSTRATED,
        "subject": "Refund for opened laptop bag",
        "order_id": "ORD-43192",
        "product_name": "Premium Leather Laptop Bag",
        "purchase_date": "2026-03-20",
        "purchase_amount": 149.99,
    },
    initial_message=(
        "I bought a Premium Leather Laptop Bag two weeks ago and I've been using it, "
        "but the stitching on the handle started coming apart after just 3 days! "
        "I want a full refund. This is unacceptable quality for a $150 bag."
    ),
    follow_up_messages=[
        "I have photos of the stitching issue. The thread is completely loose on one side. "
        "I just want my money back, this is clearly a manufacturing defect.",
        "Okay, I can send the bag back. How long will the refund take?",
    ],
    policy_keys=["refund_policy", "return_policy"],
    max_steps=5,
    expected_keywords=[
        "defect", "defective", "full refund", "replacement", "return",
        "proof", "photo", "5-7 business days", "5–7 business days",
        "shipping label", "return label",
    ],
    grading_rubric={
        "correctness": {
            "weight": 0.35,
            "criteria": [
                {"keyword_group": ["defect", "defective", "manufacturing"], "points": 0.3, "desc": "Identifies as defect"},
                {"keyword_group": ["full refund", "100%", "complete refund", "full amount"], "points": 0.3, "desc": "Offers full refund for defect"},
                {"keyword_group": ["replacement", "replace", "exchange"], "points": 0.2, "desc": "Offers replacement option"},
                {"keyword_group": ["return", "send back", "ship back"], "points": 0.2, "desc": "Explains return process"},
            ],
        },
        "tone": {
            "weight": 0.3,
            "criteria": {
                "positive_signals": [
                    "sorry", "apologize", "understand", "frustrat",
                    "inconvenience", "appreciate", "thank",
                ],
                "negative_signals": [
                    "your fault", "should have", "too bad",
                    "nothing we can do", "not our problem",
                ],
            },
        },
        "completeness": {
            "weight": 0.35,
            "criteria": [
                {"check": "addresses_defect", "points": 0.3, "desc": "Acknowledges the defect issue"},
                {"check": "explains_policy", "points": 0.25, "desc": "Explains relevant refund policy"},
                {"check": "provides_process", "points": 0.25, "desc": "Outlines the return/refund process"},
                {"check": "offers_options", "points": 0.2, "desc": "Gives customer options (refund vs replacement)"},
            ],
        },
    },
)

# ──────────────────────────────────────────────────────────────────
# TASK 3 β€” HARD: Angry Customer Complaint + Escalation
# ──────────────────────────────────────────────────────────────────

TASK_HARD_ESCALATION = _task(
    task_id="hard_escalation",
    difficulty=Difficulty.HARD,
    ticket={
        "ticket_id": "TKT-3099",
        "category": TicketCategory.COMPLAINT,
        "priority": TicketPriority.CRITICAL,
        "status": TicketStatus.OPEN,
        "customer_name": "David Martinez",
        "customer_sentiment": CustomerSentiment.ANGRY,
        "subject": "TERRIBLE experience β€” wrong item, late delivery, rude staff",
        "order_id": "ORD-67234",
        "product_name": "Smart Home Security Camera System",
        "purchase_date": "2026-03-10",
        "purchase_amount": 349.99,
    },
    initial_message=(
        "I am FURIOUS. I ordered a Smart Home Security Camera System THREE WEEKS AGO. "
        "Not only did it arrive 2 weeks late, but you sent me the WRONG ITEM β€” I got "
        "some cheap webcam instead! And when I called your support line, the agent was "
        "incredibly rude and told me to 'just return it and reorder.' This is the worst "
        "customer experience I've ever had. I want a full refund, compensation for my "
        "wasted time, AND I want to speak with a manager!"
    ),
    follow_up_messages=[
        "Don't give me the runaround. I've already wasted 2 hours on the phone with "
        "your terrible support team. I want this fixed NOW or I'm filing a complaint "
        "with consumer protection and posting this everywhere online.",

        "Fine. What exactly are you going to do to make this right? I need specifics, "
        "not more empty apologies.",

        "Okay, if you can actually guarantee that, then fine. But I want confirmation "
        "in writing via email within the hour.",
    ],
    policy_keys=["refund_policy", "return_policy", "shipping_policy", "escalation_policy"],
    max_steps=7,
    expected_keywords=[
        "sincerely apologize", "apologize", "sorry", "understand", "frustration",
        "wrong item", "full refund", "compensation", "store credit", "discount",
        "escalate", "senior", "manager", "specialist",
        "email", "confirmation", "priority", "expedited",
    ],
    grading_rubric={
        "correctness": {
            "weight": 0.3,
            "criteria": [
                {"keyword_group": ["wrong item", "incorrect", "wrong product", "mix-up"], "points": 0.2, "desc": "Acknowledges wrong item"},
                {"keyword_group": ["full refund", "refund", "money back"], "points": 0.2, "desc": "Offers refund"},
                {"keyword_group": ["compensation", "credit", "discount", "coupon"], "points": 0.2, "desc": "Offers compensation"},
                {"keyword_group": ["escalat", "senior", "manager", "specialist", "supervisor"], "points": 0.2, "desc": "Offers/performs escalation"},
                {"keyword_group": ["email", "confirmation", "writing", "written"], "points": 0.2, "desc": "Offers written confirmation"},
            ],
        },
        "tone": {
            "weight": 0.4,
            "criteria": {
                "positive_signals": [
                    "sincerely", "deeply sorry", "apologize", "understand your frustration",
                    "completely unacceptable", "you deserve better", "top priority",
                    "personally ensure", "I understand", "valid concern",
                ],
                "negative_signals": [
                    "calm down", "relax", "overreacting", "not a big deal",
                    "your fault", "policy is policy", "nothing I can do",
                    "take it or leave", "that's not true",
                ],
            },
        },
        "completeness": {
            "weight": 0.3,
            "criteria": [
                {"check": "acknowledges_all_issues", "points": 0.2, "desc": "Addresses all issues (wrong item, delay, rude staff)"},
                {"check": "concrete_resolution", "points": 0.25, "desc": "Provides concrete resolution steps"},
                {"check": "timeline", "points": 0.2, "desc": "Gives timelines for resolution"},
                {"check": "empathy", "points": 0.2, "desc": "Shows genuine empathy throughout"},
                {"check": "follow_up_plan", "points": 0.15, "desc": "Outlines follow-up plan"},
            ],
        },
    },
)


# ──────────────────────────────────────────────────────────────────
# Task registry
# ──────────────────────────────────────────────────────────────────

TASKS = {
    "easy_faq": TASK_EASY_FAQ,
    "medium_refund": TASK_MEDIUM_REFUND,
    "hard_escalation": TASK_HARD_ESCALATION,
}

TASK_IDS = list(TASKS.keys())


def get_task(task_id: str) -> Dict[str, Any]:
    """Retrieve a task definition by ID."""
    if task_id not in TASKS:
        raise ValueError(f"Unknown task_id: {task_id!r}. Available: {TASK_IDS}")
    return TASKS[task_id]