CracklesCreeper's picture
Final: openenv validate pass, 4 tasks, checklist compliant inference.py
27410ce
Raw
History Blame Contribute Delete
7.09 kB
"""
OpenSecOpsEnv Models
====================
Type-safe data structures for the SecOps incident response environment.
Follows the OpenEnv dataclass-based contract.
"""
from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from enum import Enum
from typing import Any
# ---------------------------------------------------------------------------
# Action Types
# ---------------------------------------------------------------------------
class ActionType(str, Enum):
"""All legal action types an agent can take."""
QUERY_LOGS = "query_logs"
INSPECT_METRICS = "inspect_metrics"
RESTART_SERVICE = "restart_service"
SCALE_SERVICE = "scale_service"
BLOCK_IP = "block_ip"
ROLLBACK_DEPLOYMENT = "rollback_deployment"
RUN_SECURITY_SCAN = "run_security_scan"
ISOLATE_SERVICE = "isolate_service"
SUBMIT_DIAGNOSIS = "submit_diagnosis"
# ---------------------------------------------------------------------------
# Base classes β€” pure dataclasses (OpenEnv spec compliance is via the API
# contract, not via Python inheritance.)
# ---------------------------------------------------------------------------
@dataclass
class _ActionBase:
pass
@dataclass
class _ObsBase:
pass
@dataclass
class _StateBase:
episode_id: str = field(default_factory=lambda: str(uuid.uuid4()))
step_count: int = 0
# ---------------------------------------------------------------------------
# Action
# ---------------------------------------------------------------------------
@dataclass
class SecOpsAction(_ActionBase): # type: ignore[misc]
"""
A structured action an agent can take inside the SecOps environment.
Attributes
----------
action_type : ActionType
The kind of action to perform.
parameters : dict
Action-specific parameters. Examples:
- query_logs β†’ {"service": "auth", "keywords": ["error"]}
- inspect_metrics β†’ {"service": "db"}
- restart_service β†’ {"service": "cache"}
- scale_service β†’ {"service": "api", "replicas": 5}
- block_ip β†’ {"ip": "1.2.3.4"}
- rollback_deployment β†’ {"service": "api", "version": "v1.2"}
- run_security_scan β†’ {"target": "auth"}
- isolate_service β†’ {"service": "infected_svc"}
- submit_diagnosis β†’ {"label": "cyber_attack:ddos"}
"""
action_type: str = ActionType.QUERY_LOGS.value
parameters: dict[str, Any] = field(default_factory=dict)
def __post_init__(self) -> None:
# Normalise to string value so JSON round-trips safely
if isinstance(self.action_type, ActionType):
self.action_type = self.action_type.value
# ---------------------------------------------------------------------------
# Observation
# ---------------------------------------------------------------------------
@dataclass
class ServiceMetrics:
"""Per-service numeric metrics."""
cpu: float = 0.0
memory: float = 0.0
latency: float = 0.0
error_rate: float = 0.0
@dataclass
class SecOpsObservation(_ObsBase): # type: ignore[misc]
"""
Partial observation returned to the agent at each step.
NOTE: The true root cause is *never* directly exposed here.
The agent must infer it from indirect signals.
Attributes
----------
alerts : list[dict]
Triggered monitoring alerts, e.g.
[{"service": "auth", "type": "high_cpu", "severity": "warning"}]
metrics : dict[str, dict]
Per-service metrics snapshot.
{service_name: {"cpu": …, "memory": …, "latency": …, "error_rate": …}}
logs : list[str]
Recent (and potentially noisy) log lines from relevant services.
topology : dict[str, list[str]]
Service dependency graph. {service: [downstream_deps]}
last_action_result : str
Human-readable outcome of the previous action.
time_step : int
Current time step within the episode.
available_actions : list[str]
Action types the agent may use (constant, for reference).
"""
alerts: list[dict[str, Any]] = field(default_factory=list)
metrics: dict[str, dict[str, float]] = field(default_factory=dict)
logs: list[str] = field(default_factory=list)
topology: dict[str, list[str]] = field(default_factory=dict)
last_action_result: str = ""
time_step: int = 0
available_actions: list[str] = field(
default_factory=lambda: [a.value for a in ActionType]
)
# ---------------------------------------------------------------------------
# Hidden State (internal only – never sent to agent directly)
# ---------------------------------------------------------------------------
@dataclass
class HiddenState:
"""
The ground truth that the agent must discover through investigation.
This is *not* part of the Observation; it drives environment dynamics.
"""
true_root_cause: str = "" # "infra_failure" | "misconfiguration" | "cyber_attack"
subtype: str = "" # e.g. "memory_leak", "ddos", "bad_config"
affected_services: list[str] = field(default_factory=list)
attack_progress: float = 0.0 # 0-1; relevant for cyber attacks
noise_level: float = 0.2 # fraction of misleading signals
diagnosis_submitted: bool = False
submitted_label: str = ""
# ---------------------------------------------------------------------------
# Episode State (exposed via state() endpoint)
# ---------------------------------------------------------------------------
@dataclass
class SecOpsState(_StateBase): # type: ignore[misc]
"""
Full internal state for debugging / grading. Includes hidden state.
The hidden_state dict encodes HiddenState fields as plain JSON-serialisable
primitives so callers don't need to import HiddenState.
"""
task_id: str = ""
max_steps: int = 50
cumulative_reward: float = 0.0
done: bool = False
investigation_actions: list[str] = field(default_factory=list)
mitigation_actions: list[str] = field(default_factory=list)
correct_mitigations: list[str] = field(default_factory=list)
# Ground truth (exposed in state() for graders/debuggers)
hidden_state: dict[str, Any] = field(default_factory=dict)
# Live metrics stored internally
_service_metrics: dict[str, ServiceMetrics] = field(
default_factory=dict, repr=False
)
def to_dict(self) -> dict[str, Any]:
"""Serialise to a plain dict (JSON-safe)."""
return {
"episode_id": self.episode_id,
"step_count": self.step_count,
"task_id": self.task_id,
"max_steps": self.max_steps,
"cumulative_reward": self.cumulative_reward,
"done": self.done,
"investigation_actions": self.investigation_actions,
"mitigation_actions": self.mitigation_actions,
"correct_mitigations": self.correct_mitigations,
"hidden_state": self.hidden_state,
}