forensic-shell / client.py
yashppawar's picture
Upload folder using huggingface_hub
f909af8 verified
"""ForensicShell Environment Client."""
from typing import Any, Dict
from openenv.core import EnvClient
from openenv.core.client_types import StepResult
from openenv.core.env_server.types import State
from .models import ForensicShellAction, ForensicShellObservation
class ForensicShellEnv(
EnvClient[ForensicShellAction, ForensicShellObservation, State]
):
"""
Client for the ForensicShell Environment.
Async usage:
>>> async with ForensicShellEnv(base_url="http://localhost:8000") as client:
... result = await client.reset()
... result = await client.step(
... ForensicShellAction(action_type="list_dir", path="/var/log")
... )
Docker usage:
>>> client = ForensicShellEnv.from_docker_image("forensic-shell:latest")
>>> result = await client.reset()
"""
def _step_payload(self, action: ForensicShellAction) -> Dict[str, Any]:
# Full Pydantic dump so nested report/TimelineEvent serializes correctly.
return action.model_dump(mode="json", exclude_none=False)
def _parse_result(self, payload: Dict) -> StepResult[ForensicShellObservation]:
obs_data = payload.get("observation", {}) or {}
observation = ForensicShellObservation(
output=obs_data.get("output", ""),
task_id=obs_data.get("task_id", ""),
task_description=obs_data.get("task_description", ""),
steps_remaining=obs_data.get("steps_remaining", 0),
action_error=obs_data.get("action_error"),
done=payload.get("done", False),
reward=payload.get("reward"),
metadata=obs_data.get("metadata", {}) or {},
)
return StepResult(
observation=observation,
reward=payload.get("reward"),
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),
)