File size: 5,141 Bytes
a77725d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
tasks/hard_task.py β€” Hard scenario: angry VIP customer threatening legal action
                      after a 3-week unresolved product failure

Grader used: HardTaskGrader (requires de-escalation + concrete resolution)
"""

from models import Task, StepName, DifficultyLevel

HARD_TASK = Task(
    task_id           = "HARD_001",
    difficulty        = DifficultyLevel.HARD,
    customer_emotion  = "angry",
    escalation_risk   = True,
    customer_message  = (
        "I am ABSOLUTELY FURIOUS. I purchased your premium standing desk "
        "(β‚Ή45,000) three weeks ago and it arrived broken. I've called five times, "
        "sent four emails, and NOBODY has fixed this. I'm a VIP member and this "
        "is how you treat me?! I am going to post this on every review site, "
        "contact consumer court, and make sure everyone knows how pathetic your "
        "company's service is. I want this resolved TODAY or I'm cancelling my "
        "entire β‚Ή2 lakh annual contract!"
    ),
    scenario_context  = (
        "Customer: Rajesh Mehta | VIP Tier | Annual contract: β‚Ή2,00,000. "
        "Order #ORD-VIP-2241 β€” Premium Standing Desk Pro (β‚Ή45,000) delivered "
        "on 12 March with a broken motorised lift mechanism. "
        "5 prior support tickets (TKT-001 to TKT-005) β€” all marked 'pending'. "
        "No technician dispatched yet. Replacement stock available (3–5 days). "
        "Policy: VIP customers get same-day escalation + compensation up to 20% "
        "of order value for delays > 7 days."
    ),
    required_steps    = [
        StepName.EMPATHY,
        StepName.COLLECT_INFO,
        StepName.INVESTIGATE,
        StepName.RESOLUTION,
    ],
    step_keywords     = {
        StepName.EMPATHY: [
            "i am personally and deeply sorry",
            "deeply sorry",
            "sincerely apologize",
            "i completely understand",
            "that is completely unacceptable",
            "you have every right",
            "i take full responsibility",
            "this should not have happened",
            "i hear your frustration",
        ],
        StepName.COLLECT_INFO: [
            "order number", "ticket number", "vip", "account",
            "could you confirm", "full name", "email", "contact",
        ],
        StepName.INVESTIGATE: [
            "reviewing your tickets", "i can see", "five tickets",
            "all marked pending", "this is unacceptable internally",
            "i have escalated", "our records show", "found that",
            "i found", "looking into",
        ],
        StepName.RESOLUTION: [
            "replacement", "technician today", "within 24 hours",
            "β‚Ή9,000", "9000", "20%", "compensation",
            "personally ensure", "i will personally",
            "waive", "free of charge", "priority",
        ],
    },
)


# ── Hard-task specific scoring weights ────────────────────────────────────────
HARD_TASK_SCORING = {
    "emotional_control":   0.30,   # calm + empathetic tone
    "conflict_handling":   0.25,   # de-escalation phrases
    "professional_tone":   0.20,   # no blame, no dismissal
    "resolution_accuracy": 0.25,   # correct refund % + timeline
}


def evaluate_hard_response(response: str, step: StepName) -> dict:
    """
    Returns a breakdown dict for hard-task evaluation display.
    Used by the UI to show per-dimension scores.
    """
    r      = response.lower()
    scores = {}

    if step == StepName.EMPATHY:
        emotional_hits = sum(1 for kw in HARD_TASK.step_keywords[StepName.EMPATHY]
                             if kw in r)
        scores["emotional_control"] = min(1.0, emotional_hits / 3)

        deesc = ["completely understand", "every right", "full responsibility",
                 "should not have happened", "hear your frustration"]
        deesc_hits = sum(1 for kw in deesc if kw in r)
        scores["conflict_handling"] = min(1.0, deesc_hits / 2)

        bad = ["calm down", "your fault", "nothing we can do", "policy says"]
        scores["professional_tone"] = 0.0 if any(b in r for b in bad) else 1.0

        scores["resolution_accuracy"] = 0.0   # N/A at empathy step

    elif step == StepName.RESOLUTION:
        scores["emotional_control"]   = 1.0 if "personally ensure" in r else 0.5
        scores["conflict_handling"]   = 1.0 if "priority" in r else 0.5
        scores["professional_tone"]   = 1.0 if "i will personally" in r else 0.5

        # Check resolution accuracy: compensation + timeline
        has_comp     = any(c in r for c in ["9,000", "9000", "20%", "compensation"])
        has_timeline = any(t in r for t in ["24 hours", "today", "tomorrow", "3 days"])
        scores["resolution_accuracy"] = (0.5 * int(has_comp)) + (0.5 * int(has_timeline))

    else:
        # Default neutral for mid steps
        scores = {k: 0.5 for k in HARD_TASK_SCORING}

    weighted = sum(HARD_TASK_SCORING[k] * scores.get(k, 0.0)
                   for k in HARD_TASK_SCORING)
    scores["weighted_total"] = round(weighted, 3)
    return scores