Spaces:
Sleeping
Sleeping
| """ | |
| models.py — Core data models for AI Customer Support Simulator | |
| """ | |
| from dataclasses import dataclass, field | |
| from typing import List, Optional, Dict, Any | |
| from enum import Enum | |
| class StepName(str, Enum): | |
| EMPATHY = "empathy" | |
| COLLECT_INFO = "collect_info" | |
| INVESTIGATE = "investigate" | |
| RESOLUTION = "resolution" | |
| class DifficultyLevel(str, Enum): | |
| EASY = "easy" | |
| MEDIUM = "medium" | |
| HARD = "hard" | |
| class EpisodeStatus(str, Enum): | |
| RUNNING = "running" | |
| SUCCESS = "success" | |
| FAIL = "fail" | |
| class StepResult: | |
| step: StepName | |
| agent_response: str | |
| detected_action: str # what grader detected | |
| expected_action: str # what was required | |
| correct: bool | |
| base_score: float # 0.0 – 1.0 | |
| step_bonus: float # 0.0 – 0.2 | |
| penalty: float # 0.0+ (subtracted) | |
| penalty_reasons: List[str] | |
| reward: float # base_score + step_bonus - penalty | |
| fail_triggered: bool = False | |
| fail_reason: str = "" | |
| class Episode: | |
| task_id: str | |
| difficulty: DifficultyLevel | |
| steps: List[StepResult] = field(default_factory=list) | |
| total_reward: float = 0.0 | |
| wrong_step_count: int = 0 | |
| status: EpisodeStatus = EpisodeStatus.RUNNING | |
| fail_reason: str = "" | |
| # Thresholds | |
| MAX_WRONG_STEPS: int = 3 | |
| MIN_TOTAL_REWARD: float = 1.5 # out of 4.0 max | |
| def add_step(self, result: StepResult) -> None: | |
| self.steps.append(result) | |
| self.total_reward += result.reward | |
| if not result.correct: | |
| self.wrong_step_count += 1 | |
| self._check_fail_conditions(result) | |
| def _check_fail_conditions(self, result: StepResult) -> None: | |
| if self.status != EpisodeStatus.RUNNING: | |
| return | |
| # Too many wrong steps | |
| if self.wrong_step_count >= self.MAX_WRONG_STEPS: | |
| self.status = EpisodeStatus.FAIL | |
| self.fail_reason = f"Too many wrong steps ({self.wrong_step_count}/{self.MAX_WRONG_STEPS})" | |
| return | |
| # Step-level fail | |
| if result.fail_triggered: | |
| self.status = EpisodeStatus.FAIL | |
| self.fail_reason = result.fail_reason | |
| return | |
| # All 4 steps done — check total score | |
| if len(self.steps) == 4: | |
| if self.total_reward < self.MIN_TOTAL_REWARD: | |
| self.status = EpisodeStatus.FAIL | |
| self.fail_reason = ( | |
| f"Total reward {self.total_reward:.2f} below threshold " | |
| f"{self.MIN_TOTAL_REWARD}" | |
| ) | |
| else: | |
| self.status = EpisodeStatus.SUCCESS | |
| def summary(self) -> Dict[str, Any]: | |
| return { | |
| "task_id": self.task_id, | |
| "difficulty": self.difficulty.value, | |
| "status": self.status.value, | |
| "total_reward": round(self.total_reward, 3), | |
| "wrong_steps": self.wrong_step_count, | |
| "fail_reason": self.fail_reason, | |
| "steps": [ | |
| { | |
| "step": s.step.value, | |
| "correct": s.correct, | |
| "base_score": round(s.base_score, 3), | |
| "step_bonus": round(s.step_bonus, 3), | |
| "penalty": round(s.penalty, 3), | |
| "reward": round(s.reward, 3), | |
| "penalty_reasons": s.penalty_reasons, | |
| } | |
| for s in self.steps | |
| ], | |
| } | |
| class Task: | |
| task_id: str | |
| difficulty: DifficultyLevel | |
| customer_message: str | |
| scenario_context: str | |
| required_steps: List[StepName] | |
| step_keywords: Dict[StepName, List[str]] # keywords that prove step done | |
| escalation_risk: bool = False # hard tasks only | |
| customer_emotion: str = "neutral" # neutral | frustrated | angry |