Spaces:
Running
Running
| """Task and initial-data generation for SentinelOps Arena episodes.""" | |
| import random | |
| from typing import List, Optional, Tuple | |
| from sentinelops_arena.models import ( | |
| Customer, | |
| CustomerTask, | |
| CustomerTier, | |
| Invoice, | |
| InvoiceStatus, | |
| TargetSystem, | |
| TaskType, | |
| Ticket, | |
| TicketPriority, | |
| TicketStatus, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Message templates per task type | |
| # --------------------------------------------------------------------------- | |
| _TASK_CONFIGS = [ | |
| ( | |
| TaskType.REFUND, | |
| [TargetSystem.BILLING, TargetSystem.CRM], | |
| "I'd like a refund for invoice {inv_id}. Amount: ${amount:.2f}. Reason: not satisfied with service.", | |
| ), | |
| ( | |
| TaskType.BALANCE_INQUIRY, | |
| [TargetSystem.BILLING], | |
| "Hi, can you tell me my current account balance? My customer ID is {cust_id}.", | |
| ), | |
| ( | |
| TaskType.TICKET_CHECK, | |
| [TargetSystem.TICKETING], | |
| "What's the status of my support ticket {ticket_id}?", | |
| ), | |
| ( | |
| TaskType.NEW_TICKET, | |
| [TargetSystem.TICKETING, TargetSystem.CRM], | |
| "I need help with {subject}. Please open a ticket for me.", | |
| ), | |
| ( | |
| TaskType.TIER_UPGRADE, | |
| [TargetSystem.CRM, TargetSystem.BILLING], | |
| "I believe I qualify for a tier upgrade. My customer ID is {cust_id}. Can you check?", | |
| ), | |
| ( | |
| TaskType.SLA_ESCALATION, | |
| [TargetSystem.TICKETING], | |
| "Ticket {ticket_id} is urgent and hasn't been addressed yet. Please escalate immediately.", | |
| ), | |
| ] | |
| _NEW_TICKET_SUBJECTS = [ | |
| "a billing discrepancy on my last invoice", | |
| "difficulty accessing my account dashboard", | |
| "slow response times from the API", | |
| "an incorrect charge on my statement", | |
| "missing features in my subscription plan", | |
| "data export not working properly", | |
| "integration issues with our CRM", | |
| "a security concern about my account", | |
| ] | |
| def generate_tasks( | |
| customers: List[Customer], | |
| invoices: List[Invoice], | |
| tickets: List[Ticket], | |
| num_tasks: int = 30, | |
| ) -> List[CustomerTask]: | |
| """Generate a queue of customer tasks for one episode. | |
| Each task references real customer / invoice / ticket IDs from the | |
| provided data so the worker can look them up in the simulated systems. | |
| Tasks arrive one per tick (arrival_tick == task index). | |
| """ | |
| tasks: List[CustomerTask] = [] | |
| for i in range(num_tasks): | |
| task_type, systems, template = random.choice(_TASK_CONFIGS) | |
| customer = random.choice(customers) | |
| # Build template kwargs from available data | |
| kwargs: dict = {"cust_id": customer.customer_id} | |
| if task_type == TaskType.REFUND: | |
| # Pick a random invoice (preferring ones belonging to this customer) | |
| cust_invoices = [inv for inv in invoices if inv.customer_id == customer.customer_id] | |
| invoice = random.choice(cust_invoices) if cust_invoices else random.choice(invoices) | |
| kwargs["inv_id"] = invoice.invoice_id | |
| kwargs["amount"] = invoice.amount | |
| elif task_type in (TaskType.TICKET_CHECK, TaskType.SLA_ESCALATION): | |
| cust_tickets = [t for t in tickets if t.customer_id == customer.customer_id] | |
| ticket = random.choice(cust_tickets) if cust_tickets else random.choice(tickets) | |
| kwargs["ticket_id"] = ticket.ticket_id | |
| elif task_type == TaskType.NEW_TICKET: | |
| kwargs["subject"] = random.choice(_NEW_TICKET_SUBJECTS) | |
| message = template.format(**kwargs) | |
| tasks.append( | |
| CustomerTask( | |
| task_id=f"TASK-{i:03d}", | |
| customer_id=customer.customer_id, | |
| task_type=task_type, | |
| message=message, | |
| required_systems=systems, | |
| arrival_tick=i, | |
| ) | |
| ) | |
| return tasks | |
| # --------------------------------------------------------------------------- | |
| # Initial data generation for episode reset | |
| # --------------------------------------------------------------------------- | |
| _FIRST_NAMES = [ | |
| "Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Hank", | |
| "Ivy", "Jack", "Karen", "Leo", "Mona", "Nick", "Olivia", "Pat", | |
| "Quinn", "Rita", "Sam", "Tina", | |
| ] | |
| _LAST_NAMES = [ | |
| "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", | |
| "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", | |
| "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin", | |
| ] | |
| _REGIONS = ["us-east", "us-west", "eu-west", "eu-central", "ap-southeast"] | |
| _INVOICE_ITEMS = [ | |
| "Enterprise License", "API Credits", "Support Tier", "Data Storage", | |
| "Premium Add-on", "Training Session", "Consulting Hours", "Integration Fee", | |
| ] | |
| _TICKET_SUBJECTS = [ | |
| "Cannot access dashboard", | |
| "Billing discrepancy", | |
| "API rate limit exceeded", | |
| "Data export failure", | |
| "Account lockout", | |
| "Missing invoice", | |
| "Feature request", | |
| "Performance degradation", | |
| "Integration error", | |
| "Security alert", | |
| ] | |
| def generate_initial_data( | |
| num_customers: int = 15, | |
| num_invoices: int = 15, | |
| num_tickets: int = 10, | |
| seed: Optional[int] = None, | |
| ) -> Tuple[List[Customer], List[Invoice], List[Ticket]]: | |
| """Generate random customers, invoices, and tickets for an episode reset.""" | |
| rng = random.Random(seed) | |
| # --- Customers --- | |
| customers: List[Customer] = [] | |
| for i in range(num_customers): | |
| first = rng.choice(_FIRST_NAMES) | |
| last = rng.choice(_LAST_NAMES) | |
| name = f"{first} {last}" | |
| tier = rng.choice(list(CustomerTier)) | |
| region = rng.choice(_REGIONS) | |
| customers.append( | |
| Customer( | |
| customer_id=f"C{i:03d}", | |
| name=name, | |
| tier=tier, | |
| region=region, | |
| contact_email=f"{first.lower()}.{last.lower()}@example.com", | |
| lifetime_value=round(rng.uniform(500, 50000), 2), | |
| ) | |
| ) | |
| # --- Invoices --- | |
| invoices: List[Invoice] = [] | |
| for i in range(num_invoices): | |
| cust = rng.choice(customers) | |
| num_items = rng.randint(1, 3) | |
| items = rng.sample(_INVOICE_ITEMS, min(num_items, len(_INVOICE_ITEMS))) | |
| invoices.append( | |
| Invoice( | |
| invoice_id=f"INV-{i:04d}", | |
| customer_id=cust.customer_id, | |
| amount=round(rng.uniform(50, 8000), 2), | |
| status=rng.choice(list(InvoiceStatus)), | |
| date_tick=rng.randint(0, 20), | |
| items=items, | |
| ) | |
| ) | |
| # --- Tickets --- | |
| sla_map = {TicketPriority.HIGH: 6, TicketPriority.MEDIUM: 12, TicketPriority.LOW: 18} | |
| tickets: List[Ticket] = [] | |
| for i in range(num_tickets): | |
| cust = rng.choice(customers) | |
| priority = rng.choice(list(TicketPriority)) | |
| created_tick = rng.randint(0, 10) | |
| tickets.append( | |
| Ticket( | |
| ticket_id=f"TK-{i:03d}", | |
| customer_id=cust.customer_id, | |
| subject=rng.choice(_TICKET_SUBJECTS), | |
| priority=priority, | |
| status=rng.choice(list(TicketStatus)), | |
| created_tick=created_tick, | |
| sla_deadline_tick=created_tick + sla_map[priority], | |
| data_region=cust.region, | |
| ) | |
| ) | |
| return customers, invoices, tickets | |