cloudsense / client.py
Jaswanth1210's picture
Add client SDK, fix openenv.yaml format, add model aliases
e8f5d33
"""CloudSense Python SDK — typed client for the CloudSense RL environment."""
from __future__ import annotations
import requests
from env.models import (
ActionType,
CloudAction,
CloudObservation,
CloudSenseAction,
CloudSenseObservation,
StepResult,
)
class CloudSenseClient:
"""Typed HTTP client for the CloudSense environment API."""
def __init__(self, base_url: str = "http://localhost:7860"):
self.base_url = base_url.rstrip("/")
self._session = requests.Session()
# ── Core environment API ────────────────────────────────────────
def reset(self, task_id: str = "startup-cleanup") -> CloudObservation:
"""Reset the environment to a new episode."""
resp = self._session.post(
f"{self.base_url}/reset",
params={"task_id": task_id},
timeout=30,
)
resp.raise_for_status()
return CloudObservation(**resp.json())
def step(self, action: CloudAction) -> StepResult:
"""Execute an action and return the result."""
resp = self._session.post(
f"{self.base_url}/step",
json=action.model_dump(),
timeout=30,
)
resp.raise_for_status()
data = resp.json()
return StepResult(
observation=CloudObservation(**data["observation"]),
reward=data["reward"],
done=data["done"],
info=data.get("info", {}),
)
def state(self) -> dict:
"""Get the current environment state."""
resp = self._session.get(f"{self.base_url}/state", timeout=10)
resp.raise_for_status()
return resp.json()
def close(self) -> None:
"""Close the current episode."""
resp = self._session.post(f"{self.base_url}/close", timeout=10)
resp.raise_for_status()
# ── Discovery ───────────────────────────────────────────────────
def health(self) -> dict:
"""Check environment health."""
resp = self._session.get(f"{self.base_url}/health", timeout=10)
resp.raise_for_status()
return resp.json()
def tasks(self) -> list[dict]:
"""List available tasks."""
resp = self._session.get(f"{self.base_url}/tasks", timeout=10)
resp.raise_for_status()
return resp.json()
# ── Convenience ─────────────────────────────────────────────────
def rightsize(
self, resource_id: str, instance_type: str, reasoning: str = "", **config
) -> StepResult:
"""Shortcut: rightsize a resource."""
new_config = {"instance_type": instance_type, **config}
return self.step(
CloudAction(
action_type=ActionType.rightsize_resource,
resource_id=resource_id,
new_config=new_config,
reasoning=reasoning,
)
)
def terminate(self, resource_id: str, reasoning: str = "") -> StepResult:
"""Shortcut: terminate a resource."""
return self.step(
CloudAction(
action_type=ActionType.terminate_resource,
resource_id=resource_id,
reasoning=reasoning,
)
)
def skip(self, resource_id: str, reasoning: str = "") -> StepResult:
"""Shortcut: skip a resource."""
return self.step(
CloudAction(
action_type=ActionType.skip_resource,
resource_id=resource_id,
reasoning=reasoning,
)
)