"""CI/CD Doctor Environment Client.""" from typing import Dict from pydantic import BaseModel from openenv.core import EnvClient from openenv.core.client_types import StepResult from openenv.core.env_server.types import State class CiCdDoctorAction(BaseModel): command: str class CiCdDoctorObservation(BaseModel): stdout: str = "" exit_code: int = 0 pipeline_status: str = "not_run" steps_remaining: int = 15 done: bool = False reward: float = 0.0 class CiCdDoctorEnv(EnvClient[CiCdDoctorAction, CiCdDoctorObservation, State]): """ Client for the CI/CD Doctor environment. Maintains a persistent WebSocket connection to the environment server. Example: >>> with CiCdDoctorEnv(base_url="http://localhost:8000") as client: ... result = client.reset() ... print(result.observation.stdout) ... ... result = client.step(CiCdDoctorAction(command="cat requirements.txt")) ... print(result.observation.stdout) Example with Docker: >>> client = CiCdDoctorEnv.from_docker_image("cicd-doctor-env:latest") >>> try: ... result = client.reset() ... result = client.step(CiCdDoctorAction(command="pipeline run")) ... finally: ... client.close() """ def _step_payload(self, action: CiCdDoctorAction) -> Dict: return {"command": action.command} def _parse_result(self, payload: Dict) -> StepResult[CiCdDoctorObservation]: obs_data = payload.get("observation", {}) observation = CiCdDoctorObservation( stdout=obs_data.get("stdout", ""), exit_code=obs_data.get("exit_code", 0), pipeline_status=obs_data.get("pipeline_status", "not_run"), steps_remaining=obs_data.get("steps_remaining", 15), done=payload.get("done", False), reward=payload.get("reward", 0.0), ) return StepResult( observation=observation, reward=payload.get("reward", 0.0), done=payload.get("done", False), ) def _parse_state(self, payload: Dict) -> State: return State( episode_id=payload.get("episode_id"), step_count=payload.get("step_count", 0), )