sentinel_env / models.py
KaushikSarveswaran's picture
Initial submission: OpenEnv-Sentinel SRE triage environment
33dd3ee
"""Pydantic models for OpenEnv-Sentinel.
Typed discriminated union for actions β€” each tool has its own action + params class.
"""
from typing import Annotated, List, Literal, Union
from openenv.core.env_server.types import Action, Observation, State
from pydantic import BaseModel, Field, RootModel
# ── Parameter models ────────────────────────────────────────────────
class QueryLogsParams(BaseModel):
service: str
query: str = "all"
severity: str = "all"
class QueryMetricsParams(BaseModel):
service: str
metric: str
class GetServiceStatusParams(BaseModel):
service: str
class GetDependencyMapParams(BaseModel):
service: str = ""
class ConsultRunbookParams(BaseModel):
topic: str
class CheckRecentChangesParams(BaseModel):
service: str = ""
class SubmitResolutionParams(BaseModel):
root_cause: str
affected_service: str
recommendation: str
# ── Action models ───────────────────────────────────────────────────
class QueryLogsAction(Action):
tool_name: Literal["query_logs"] = "query_logs"
parameters: QueryLogsParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class QueryMetricsAction(Action):
tool_name: Literal["query_metrics"] = "query_metrics"
parameters: QueryMetricsParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class GetServiceStatusAction(Action):
tool_name: Literal["get_service_status"] = "get_service_status"
parameters: GetServiceStatusParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class GetDependencyMapAction(Action):
tool_name: Literal["get_dependency_map"] = "get_dependency_map"
parameters: GetDependencyMapParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class ConsultRunbookAction(Action):
tool_name: Literal["consult_runbook"] = "consult_runbook"
parameters: ConsultRunbookParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class CheckRecentChangesAction(Action):
tool_name: Literal["check_recent_changes"] = "check_recent_changes"
parameters: CheckRecentChangesParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
class SubmitResolutionAction(Action):
tool_name: Literal["submit_resolution"] = "submit_resolution"
parameters: SubmitResolutionParams
def param_dict(self) -> dict:
return self.parameters.model_dump()
# ── Discriminated union ─────────────────────────────────────────────
_ActionUnion = Annotated[
Union[
QueryLogsAction,
QueryMetricsAction,
GetServiceStatusAction,
GetDependencyMapAction,
ConsultRunbookAction,
CheckRecentChangesAction,
SubmitResolutionAction,
],
Field(discriminator="tool_name"),
]
class SentinelAction(RootModel[_ActionUnion]):
"""Discriminated union action β€” delegates to the matched concrete action."""
@property
def tool_name(self) -> str:
return self.root.tool_name
def param_dict(self) -> dict:
return self.root.param_dict()
# ── Observation & State ─────────────────────────────────────────────
class SentinelObservation(Observation):
"""What the agent sees after each step."""
incident_summary: str = Field(default="", description="Initial alert / ongoing context")
tool_output: str = Field(default="", description="Result from the last tool call")
available_tools: List[str] = Field(default_factory=list, description="Tools the agent can use")
step_number: int = Field(default=0, description="Current step number")
max_steps: int = Field(default=20, description="Maximum steps per episode")
cumulative_reward: float = Field(default=0.0, description="Running total of per-step rewards")
last_action_error: str = Field(default="", description="Error from last invalid action")
tool_descriptions: dict = Field(default_factory=dict, description="Parameter metadata (populated on reset only)")
class SentinelState(State):
"""Internal environment state."""
task_id: int = 1
task_name: str = ""
tools_called: List[str] = Field(default_factory=list)
relevant_tools_called: List[str] = Field(default_factory=list)
resolution_submitted: bool = False
root_cause_correct: bool = False
recommendation_correct: bool = False
final_score: float = 0.0