sre-openenv / client.py
Dragonfire146's picture
Initial commit
9eb0831
"""
SRE OpenEnv Client.
Provides the SREEnv client class for interacting with a running
SRE environment instance via HTTP.
This implementation uses 'requests' directly to maintain compatibility
with synchronous agent loops while aligning with OpenEnv 0.1 types.
"""
from __future__ import annotations
import requests
from typing import Any, Optional
from dataclasses import asdict, is_dataclass
try:
from openenv.core.client_types import StepResult
except ImportError:
# Handle legacy openenv_core or shim
try:
from openenv_core.client_types import StepResult
except ImportError:
# Fallback for older versions if necessary
from openenv_core import StepResult
from models import SREAction, SREObservation, SREState
class SREEnv:
"""
Synchronous client for the Autonomous SRE OpenEnv environment.
Compatible with OpenEnv 0.1+ server endpoints (/reset, /step, /state).
"""
def __init__(self, base_url: str = "http://localhost:8000"):
self.base_url = base_url.rstrip("/")
self._session = requests.Session()
def reset(self, seed: int | None = None, task_id: str | None = None) -> StepResult:
"""Reset the environment via POST /reset."""
payload = {}
if seed is not None:
payload["seed"] = seed
if task_id is not None:
payload["task_id"] = task_id
response = self._session.post(f"{self.base_url}/reset", json=payload)
response.raise_for_status()
return self._parse_result(response.json())
def step(self, action: SREAction) -> StepResult[SREObservation]:
"""Execute an action via POST /step."""
payload = {
"action": self._step_payload(action)
}
r = self._session.post(f"{self.base_url}/step", json=payload)
r.raise_for_status()
return self._parse_result(r.json())
@property
def state(self) -> SREState:
"""Retrieve current environment state via GET /state."""
r = self._session.get(f"{self.base_url}/state")
r.raise_for_status()
return self._parse_state(r.json())
def _step_payload(self, action: Any) -> dict:
"""Serialize an action to a JSON-compatible dict (Dataclass/Pydantic compatible)."""
if is_dataclass(action):
return asdict(action)
if hasattr(action, "model_dump"):
return action.model_dump()
if hasattr(action, "dict"):
return action.dict()
return vars(action)
def _parse_result(self, payload: dict) -> StepResult[SREObservation]:
"""Parse the server response into a typed StepResult."""
# OpenEnv 0.1 server returns { "observation": {...}, "reward": float, "done": bool }
obs_data = payload.get("observation", {})
observation = SREObservation(**obs_data)
return StepResult(
observation=observation,
reward=payload.get("reward", 0.0),
done=payload.get("done", False),
)
def _parse_state(self, payload: dict) -> SREState:
"""Parse the server state response into a typed SREState."""
return SREState(**payload)
def close(self):
"""Close the session."""
self._session.close()