ahagestedt/apex-devops
0
1"""2OpenEnv-compatible Action, Observation, and State types for multi-step QA evaluation3environments.4 5Architecture6------------7Each episode runs up to 8 steps across 4 sequential phases:8 9 Phase 0 — review : Agent inspects the ticket and customer history (GET only).10 Phase 1 — analyze : Agent gathers rubric, similar cases, API docs, and may11 submit a preliminary evaluation (GET + POST).12 Phase 2 — decide : Agent submits its final evaluation (POST only).13 Phase 3 — complete : Agent finishes the episode (FINISH only).14 15Action types:16 GET — Retrieve a named resource (ticket, customer_history, rubric, ...).17 POST — Submit evaluation data (reasoning, score, confidence, notes).18 FINISH — End the episode.19 20Reward is a weighted sum of 7 independent components (see REWARD_COMPONENTS) that21measure completeness, accuracy, reasoning quality, confidence calibration, safety22compliance, efficiency, and gating correctness.23 24Note: openenv.core 0.2.x uses Pydantic BaseModel (not dataclasses).25"""26 27from __future__ import annotations28 29from typing import Any, Dict, List, Optional30 31from pydantic import Field32 33from openenv.core.env_server.types import Action, Observation, State34 35# ---------------------------------------------------------------------------36# Constants37# ---------------------------------------------------------------------------38 39PHASES: List[Dict[str, Any]] = [40 {41 "name": "review",42 "index": 0,43 "allowed_actions": ["GET", "FINISH"],44 "allowed_resources": ["ticket", "customer_history"],45 },46 {47 "name": "analyze",48 "index": 1,49 "allowed_actions": ["GET", "POST", "FINISH"],50 "allowed_resources": ["rubric", "similar_cases", "api_docs"],51 },52 {53 "name": "decide",54 "index": 2,55 "allowed_actions": ["POST", "FINISH"],56 "allowed_resources": [],57 },58 {59 "name": "complete",60 "index": 3,61 "allowed_actions": ["FINISH"],62 "allowed_resources": [],63 },64]65 66REWARD_COMPONENTS: Dict[str, Dict[str, Any]] = {67 "completeness": {68 "weight": 0.20,69 "range": (0.0, 1.0),70 "description": "Did agent gather all available info before deciding?",71 },72 "accuracy": {73 "weight": 0.25,74 "range": (0.0, 1.0),75 "description": "Correctness of evaluation score vs ground truth",76 },77 "reasoning_quality": {78 "weight": 0.15,79 "range": (0.0, 1.0),80 "description": "Depth and coherence of reasoning steps",81 },82 "confidence_calibration": {83 "weight": 0.10,84 "range": (0.0, 1.0),85 "description": "How well confidence matches actual accuracy",86 },87 "safety_compliance": {88 "weight": 0.15,89 "range": (-1.0, 1.0),90 "description": "Penalty for missing safety violations",91 },92 "efficiency": {93 "weight": 0.05,94 "range": (0.0, 1.0),95 "description": "Fewer steps = higher score",96 },97 "gating_correctness": {98 "weight": 0.10,99 "range": (0.0, 1.0),100 "description": "Did gating decision match ideal routing?",101 },102}103 104 105# ---------------------------------------------------------------------------106# Action107# ---------------------------------------------------------------------------108 109 110class QAAction(Action):111 """Action the agent sends to the environment each step.112 113 Three action types drive the multi-step episode:114 115 * **GET** — Retrieve a named resource into ``gathered_info``.116 * **POST** — Submit evaluation data (reasoning, score, confidence, notes).117 * **FINISH** — Signal the end of the episode.118 119 Inherits ``metadata`` from :class:`Action`.120 """121 122 action_type: str = Field(123 description=(124 "Type of action to execute. "125 "One of 'GET' (retrieve a resource), "126 "'POST' (submit evaluation data), "127 "or 'FINISH' (end the episode)."128 ),129 )130 131 resource: Optional[str] = Field(132 default=None,133 description=(134 "Resource to retrieve when action_type is 'GET'. "135 "One of: ticket, customer_history, rubric, similar_cases, api_docs."136 ),137 )138 139 evaluation_data: Optional[Dict[str, Any]] = Field(140 default=None,141 description=(142 "Evaluation payload when action_type is 'POST'. "143 "Expected keys: reasoning (str), score (float), "144 "confidence (float 0-1), notes (str)."145 ),146 )147 148 149# ---------------------------------------------------------------------------150# Observation151# ---------------------------------------------------------------------------152 153 154class QAObservation(Observation):155 """What the agent observes after each step.156 157 Contains phase context, available actions/resources for the current phase,158 returned data from GET/POST actions, and shaped reward components once the159 episode is evaluated.160 161 Inherits ``done``, ``reward``, and ``metadata`` from :class:`Observation`.162 """163 164 # --- Scenario identity ---------------------------------------------------165 166 scenario_id: str = Field(167 default="",168 description="Unique scenario identifier, e.g. 'MFG-QB-001'.",169 )170 171 software: str = Field(172 default="",173 description="Software product within the enterprise, e.g. 'QuickBooks'.",174 )175 176 workflow: str = Field(177 default="",178 description="Workflow name, e.g. 'Create Invoice'.",179 )180 181 enterprise: str = Field(182 default="",183 description="Enterprise name, e.g. 'Intuit'.",184 )185 186 domain: str = Field(187 default="",188 description="Domain identifier, e.g. 'fin-sim', 'dev-sim'.",189 )190 191 # --- Phase tracking -------------------------------------------------------192 193 phase: str = Field(194 default="review",195 description="Current phase: 'review', 'analyze', 'decide', or 'complete'.",196 )197 198 phase_index: int = Field(199 default=0,200 description="Numeric index of the current phase (0-3).",201 )202 203 available_actions: List[str] = Field(204 default_factory=list,205 description="Action types valid in the current phase, e.g. ['GET', 'FINISH'].",206 )207 208 available_resources: List[str] = Field(209 default_factory=list,210 description="GET resources available in the current phase.",211 )212 213 # --- Step context ---------------------------------------------------------214 215 context: Dict[str, Any] = Field(216 default_factory=dict,217 description="Phase-specific data returned to the agent after each action.",218 )219 220 step_number: int = Field(221 default=0,222 description="Current step in the episode (0-based).",223 )224 225 max_steps: int = Field(226 default=8,227 description="Maximum steps allowed per episode (always 8).",228 )229 230 # --- Reward & evaluation --------------------------------------------------231 232 reward_components: Optional[Dict[str, float]] = Field(233 default=None,234 description=(235 "7-component shaped reward breakdown, keyed by component name. "236 "Populated after the agent submits an evaluation."237 ),238 )239 240 total_reward: Optional[float] = Field(241 default=None,242 description="Aggregate weighted reward across all components.",243 )244 245 verifier_results: Optional[Dict[str, Any]] = Field(246 default=None,247 description="Results from task-type verifiers (pass/fail per check).",248 )249 250 251# ---------------------------------------------------------------------------252# State253# ---------------------------------------------------------------------------254 255 256class QAState(State):257 """Internal environment state that persists across all steps in an episode.258 259 Tracks the scenario definition, which phase the agent is in, what260 information the agent has gathered, its submitted evaluation, reward261 breakdown, and the full action history.262 263 Inherits ``episode_id`` and ``step_count`` from :class:`State`.264 """265 266 # --- Scenario & identity --------------------------------------------------267 268 scenario: Dict[str, Any] = Field(269 default_factory=dict,270 description="Full scenario definition loaded from the data source.",271 )272 273 enterprise: str = Field(274 default="",275 description="Enterprise name, e.g. 'Intuit'.",276 )277 278 domain: str = Field(279 default="",280 description="Domain identifier, e.g. 'fin-sim', 'dev-sim'.",281 )282 283 # --- Phase tracking -------------------------------------------------------284 285 phase: str = Field(286 default="review",287 description="Current phase: 'review', 'analyze', 'decide', or 'complete'.",288 )289 290 phase_index: int = Field(291 default=0,292 description="Numeric index of the current phase (0-3).",293 )294 295 # --- Agent progress -------------------------------------------------------296 297 gathered_info: Dict[str, Any] = Field(298 default_factory=dict,299 description=(300 "Information the agent has retrieved via GET actions, "301 "keyed by resource name (e.g. 'ticket', 'rubric')."302 ),303 )304 305 submitted_evaluation: Optional[Dict[str, Any]] = Field(306 default=None,307 description="Agent's submitted evaluation data from a POST action.",308 )309 310 action_history: List[Dict[str, Any]] = Field(311 default_factory=list,312 description=(313 "Chronological log of every action taken in this episode. "314 "Each entry contains action_type, resource (if GET), step, and phase."315 ),316 )317 318 # --- Reward & evaluation --------------------------------------------------319 320 reward_breakdown: Optional[Dict[str, float]] = Field(321 default=None,322 description="7-component shaped reward breakdown after evaluation.",323 )324 325 verifier_results: Optional[Dict[str, Any]] = Field(326 default=None,327 description="Verifier pass/fail results per task-type check.",328 )329 330 gating_decision: Optional[str] = Field(331 default=None,332 description="Routing decision: 'auto_commit' or 'route_to_hitl'.",333 )334 335 # --- Episode control ------------------------------------------------------336 337 is_done: bool = Field(338 default=False,339 description="Whether the episode has finished.",340 )341 