Spaces:
Sleeping
Sleeping
File size: 5,054 Bytes
06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 f9cf02d 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 75ca235 06aac03 711b9b5 06aac03 711b9b5 06aac03 75ca235 cfe0896 06aac03 75ca235 06aac03 75ca235 06aac03 | 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 | """
Pydantic models for the Bug Triage OpenEnv environment.
"""
from datetime import datetime
from typing import Literal, Optional
from pydantic import BaseModel, Field
class TicketModel(BaseModel):
"""Represents a bug ticket in the system."""
ticket_id: str
title: str
description: str
reporter_type: Literal["user", "qa", "monitoring"]
service: str
component_candidates: list[str]
created_at: datetime
customer_tier: Literal["free", "pro", "enterprise"]
repro_steps_present: bool
logs_present: bool
attachments_count: int
suspected_duplicate_ids: list[str]
class TicketGroundTruth(BaseModel):
"""Hidden ground truth for a ticket."""
ticket_id: str
true_severity: Literal["sev0", "sev1", "sev2", "sev3"]
true_priority: Literal["p0", "p1", "p2", "p3"]
true_component: str
true_assignee_team: str
duplicate_of: Optional[str] = None
needs_more_info: bool
class CurrentTicketModel(TicketModel):
"""Current ticket being focused on (inherits all fields from TicketModel)."""
class QueueStatsModel(BaseModel):
"""Statistics about the ticket queue."""
remaining_count: int
urgent_count: int
sla_at_risk_count: int
class ObservationModel(BaseModel):
"""Observation returned by the environment."""
current_ticket: Optional[CurrentTicketModel] = None
queue_stats: QueueStatsModel
last_action_result: Optional[str] = None
available_teams: list[str]
available_components: list[str]
steps_used: int
steps_remaining: int
partial_score: Optional[float] = None
class ClassifyAction(BaseModel):
"""Classify ticket with severity, priority, and component."""
severity: Literal["sev0", "sev1", "sev2", "sev3"]
priority: Literal["p0", "p1", "p2", "p3"]
component: str
class AssignAction(BaseModel):
"""Assign ticket to a team."""
team: str
class MarkDuplicateAction(BaseModel):
"""Mark ticket as duplicate of another."""
canonical_ticket_id: str
class RequestInfoAction(BaseModel):
"""Request more information from reporter."""
info_type: Literal["repro_steps", "logs", "both"]
class DeferAction(BaseModel):
"""Defer ticket to backlog."""
reason: str
class CloseAction(BaseModel):
"""Close ticket with reason."""
reason: Literal["invalid", "wont_fix", "cannot_reproduce", "resolved"]
class EscalateAction(BaseModel):
"""Escalate ticket as urgent incident."""
justification: str
class NextTicketAction(BaseModel):
"""Move to next ticket."""
pass
class ActionModel(BaseModel):
"""Action that can be taken in the environment."""
action_type: Literal[
"classify",
"assign",
"mark_duplicate",
"request_info",
"defer",
"close",
"escalate_incident",
"next_ticket",
]
classify: Optional[ClassifyAction] = None
assign: Optional[AssignAction] = None
mark_duplicate: Optional[MarkDuplicateAction] = None
request_info: Optional[RequestInfoAction] = None
defer: Optional[DeferAction] = None
close: Optional[CloseAction] = None
escalate_incident: Optional[EscalateAction] = None
next_ticket: Optional[NextTicketAction] = None
def model_post_init(self, __context):
"""Validate that the action payload matches the action type."""
action_map = {
"classify": self.classify,
"assign": self.assign,
"mark_duplicate": self.mark_duplicate,
"request_info": self.request_info,
"defer": self.defer,
"close": self.close,
"escalate_incident": self.escalate_incident,
"next_ticket": self.next_ticket,
}
expected_field = action_map.get(self.action_type)
if expected_field is None and self.action_type != "next_ticket":
raise ValueError(f"Missing payload for action_type '{self.action_type}'")
unexpected_fields = [
field_name
for field_name, payload in action_map.items()
if field_name != self.action_type and payload is not None
]
if unexpected_fields:
extras = ", ".join(sorted(unexpected_fields))
raise ValueError(
f"Unexpected payload field(s) for action_type '{self.action_type}': {extras}"
)
class RewardModel(BaseModel):
"""Reward information for a step."""
step_reward: float = Field(..., ge=0.0, le=1.0)
cumulative_reward: float
reward_breakdown: dict[str, float] = Field(default_factory=dict)
class TicketStateModel(BaseModel):
"""State of a single ticket in the triage process."""
ticket_id: str
triaged: bool
actions_taken: list[str]
class StateModel(BaseModel):
"""Full state of the environment."""
current_task_id: str
current_ticket_index: int
total_tickets: int
tickets_state: list[TicketStateModel]
steps_used: int
steps_remaining: int
cumulative_reward: float
episode_done: bool
|