Spaces:
Running
Running
File size: 8,767 Bytes
3752981 043d9e1 c64d203 67ce1eb 8241eb5 454cef3 3752981 8241eb5 043d9e1 454cef3 3752981 8241eb5 3752981 c64d203 3752981 c64d203 3752981 67ce1eb c64d203 3752981 c64d203 3752981 c64d203 67ce1eb 3752981 67ce1eb 3752981 42dd095 6c5051f 42dd095 67ce1eb c64d203 67ce1eb 8241eb5 c64d203 67ce1eb 8241eb5 1d9d3ee 043d9e1 1d9d3ee 42dd095 | 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 | from __future__ import annotations
from typing import Any, Optional
from pydantic import BaseModel, Field, field_validator
from openenv.core.env_server.types import Action, Observation, State
from vocabulary import (
ASSIGNMENT_GROUPS,
ISSUE_TYPES,
PRIORITIES,
RESOLUTION_ACTIONS,
)
ISSUE_TYPE_SET = set(ISSUE_TYPES)
PRIORITY_SET = set(PRIORITIES)
ASSIGNMENT_GROUP_SET = set(ASSIGNMENT_GROUPS)
RESOLUTION_ACTION_SET = set(RESOLUTION_ACTIONS)
ACTION_TYPE_SET = {
"submit",
"investigate",
"request_info",
"defer",
"open_incident",
}
TOOL_NAME_SET = {"lookup_related_ticket", "lookup_requester_history"}
TOOL_NAME_SET.add("lookup_internal_routing_note")
TOOL_NAME_SET.add("lookup_queue_capacity_forecast")
TOOL_NAME_SET.add("lookup_queue_cluster_summary")
def _validate_choice(value: str, allowed: set[str], field_name: str) -> str:
if value not in allowed:
allowed_values = ", ".join(sorted(allowed))
raise ValueError(f"{field_name} must be one of: {allowed_values}")
return value
def _validate_optional_choice(
value: Optional[str], allowed: set[str], field_name: str
) -> Optional[str]:
if value is None:
return None
return _validate_choice(value, allowed, field_name)
class HelpdeskTicketRecord(BaseModel):
ticket_id: str
title: str
requester: str
description: str
issue_type: str
priority: str
assignment_group: str
resolution_action: str
ambiguity_note: Optional[str] = None
related_ticket_id: Optional[str] = None
planning_note: Optional[str] = None
alternate_issue_type: Optional[str] = None
alternate_priority: Optional[str] = None
alternate_assignment_group: Optional[str] = None
alternate_resolution_action: Optional[str] = None
alternate_route_score_multiplier: float = 0.0
customer_update_note: Optional[str] = None
incident_recommended: bool = False
generated_from_ticket_id: Optional[str] = None
service_cluster_id: Optional[str] = None
@field_validator("issue_type")
@classmethod
def validate_issue_type(cls, value: str) -> str:
return _validate_choice(value, ISSUE_TYPE_SET, "issue_type")
@field_validator("priority")
@classmethod
def validate_priority(cls, value: str) -> str:
return _validate_choice(value, PRIORITY_SET, "priority")
@field_validator("assignment_group")
@classmethod
def validate_assignment_group(cls, value: str) -> str:
return _validate_choice(value, ASSIGNMENT_GROUP_SET, "assignment_group")
@field_validator("resolution_action")
@classmethod
def validate_resolution_action(cls, value: str) -> str:
return _validate_choice(value, RESOLUTION_ACTION_SET, "resolution_action")
@field_validator("alternate_issue_type")
@classmethod
def validate_alternate_issue_type(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, ISSUE_TYPE_SET, "alternate_issue_type")
@field_validator("alternate_priority")
@classmethod
def validate_alternate_priority(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, PRIORITY_SET, "alternate_priority")
@field_validator("alternate_assignment_group")
@classmethod
def validate_alternate_assignment_group(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(
value,
ASSIGNMENT_GROUP_SET,
"alternate_assignment_group",
)
@field_validator("alternate_resolution_action")
@classmethod
def validate_alternate_resolution_action(
cls,
value: Optional[str],
) -> Optional[str]:
return _validate_optional_choice(
value,
RESOLUTION_ACTION_SET,
"alternate_resolution_action",
)
@field_validator("alternate_route_score_multiplier")
@classmethod
def validate_alternate_route_score_multiplier(cls, value: float) -> float:
if not 0.0 <= value <= 1.0:
raise ValueError("alternate_route_score_multiplier must be in [0.0, 1.0]")
return value
class HelpdeskTicketAction(Action):
action_type: str = "submit"
tool_name: Optional[str] = None
tool_target_ticket_id: Optional[str] = None
issue_type: Optional[str] = None
priority: Optional[str] = None
assignment_group: Optional[str] = None
resolution_action: Optional[str] = None
@field_validator("action_type")
@classmethod
def validate_action_type(cls, value: str) -> str:
return _validate_choice(value, ACTION_TYPE_SET, "action_type")
@field_validator("tool_name")
@classmethod
def validate_tool_name(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, TOOL_NAME_SET, "tool_name")
@field_validator("issue_type")
@classmethod
def validate_issue_type(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, ISSUE_TYPE_SET, "issue_type")
@field_validator("priority")
@classmethod
def validate_priority(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, PRIORITY_SET, "priority")
@field_validator("assignment_group")
@classmethod
def validate_assignment_group(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, ASSIGNMENT_GROUP_SET, "assignment_group")
@field_validator("resolution_action")
@classmethod
def validate_resolution_action(cls, value: Optional[str]) -> Optional[str]:
return _validate_optional_choice(value, RESOLUTION_ACTION_SET, "resolution_action")
class HelpdeskTicketObservation(Observation):
task_id: int = 0
task_name: str = ""
instructions: str = ""
allowed_fields: list[str] = Field(default_factory=list)
available_action_types: list[str] = Field(default_factory=list)
available_tools: list[str] = Field(default_factory=list)
investigation_budget_remaining: int = 0
last_tool_result: Optional[dict[str, Any]] = None
current_ticket: Optional[dict[str, Any]] = None
queue_size: int = 0
tickets_remaining: int = 0
tickets_after_current: int = 0
tickets_processed: int = 0
queue_position: int = 0
average_score_so_far: float = 0.0
progress_fraction: float = 0.0
history: list[dict[str, Any]] = Field(default_factory=list)
last_reward_components: dict[str, Any] = Field(default_factory=dict)
rubric_reward: Optional[float] = None
class HelpdeskTicketState(State):
current_task_id: Optional[int] = None
seed: Optional[int] = None
queue_ticket_ids: list[str] = Field(default_factory=list)
current_ticket_index: int = 0
per_ticket_scores: list[float] = Field(default_factory=list)
total_reward: float = 0.0
last_step_reward: Optional[float] = None
# `reward` is the field the evaluator checks on GET /state (mentor spec)
reward: Optional[float] = None
done: bool = False
average_score_so_far: float = 0.0
investigation_steps: int = 0
investigation_budget_remaining: int = 0
investigation_penalty_applied: float = 0.0
planning_penalty_applied: float = 0.0
last_tool_result: Optional[dict[str, Any]] = None
last_reward_components: dict[str, Any] = Field(default_factory=dict)
ticket_tool_usage: dict[str, list[str]] = Field(default_factory=dict)
team_capacity_initial: dict[str, int] = Field(default_factory=dict)
team_capacity_remaining: dict[str, int] = Field(default_factory=dict)
high_priority_slots_initial: int = 0
high_priority_slots_remaining: int = 0
escalation_slots_initial: int = 0
escalation_slots_remaining: int = 0
planning_penalty_total: float = 0.0
capacity_pressure_tickets_resolved: int = 0
cluster_stabilizations_total: int = 0
cluster_destabilizations_total: int = 0
ticket_request_info_usage: dict[str, int] = Field(default_factory=dict)
ticket_defer_counts: dict[str, int] = Field(default_factory=dict)
open_incident_ticket_ids: list[str] = Field(default_factory=list)
incident_slots_initial: int = 0
incident_slots_remaining: int = 0
incident_actions_used: int = 0
incident_gap_total: float = 0.0
deferred_ticket_count: int = 0
sla_breach_count: int = 0
spawned_follow_up_ticket_ids: list[str] = Field(default_factory=list)
spawned_follow_up_source_ids: list[str] = Field(default_factory=list)
dynamic_queue_events: list[dict[str, Any]] = Field(default_factory=list)
queue_management_score: float = 0.0
queue_management_breakdown: dict[str, Any] = Field(default_factory=dict)
history_entries: list[dict] = Field(default_factory=list)
|