3sacrowd / src /environment.py
DevOps Team
Initial commit with fixed /reset
ac8898f
Raw History Blame Contribute Delete
4.36 kB
from typing import Dict, List, Any
from .virtual_filesystem import SystemStore
from .terminal_emulator import Shell
from .tasks import (
Objective, ScenarioTask,
get_task, all_task_keys, task_metadata,
)
from .scenarios import list_scenarios
class TrainingEnv:
def __init__(self, scenario: str = "log_analysis"):
"""
Create environment for a given scenario key.
"""
key = scenario
self.task = get_task(key)
self.difficulty = self.task.diff
self.storage = SystemStore()
self.terminal = Shell(self.storage)
self.step_count = 0
self.limit = 50
self.finished = False
self.score = 0.0
self._scenario_key = key
# scenario tasks may override max_steps
if isinstance(self.task, ScenarioTask):
self.limit = self.task.scenario.max_steps
def reset(self) -> Dict[str, Any]:
self.storage.clear()
self.terminal = Shell(self.storage)
self.step_count = 0
self.finished = False
self.score = 0.0
# for scenario tasks, inject faults into the fresh environment
if isinstance(self.task, ScenarioTask):
self.task.setup(self.storage, self.terminal)
view = self._view()
return {
"observation": view,
"info": {
"task_name": self.task.nm,
"difficulty": self.difficulty,
"instructions": self.task.guide(),
"max_steps": self.limit,
"scenario_key": self._scenario_key,
}
}
def step(self, cmd: str) -> Dict[str, Any]:
if self.finished:
return {
"observation": self._view(),
"reward": 0.0,
"done": True,
"info": {"error": "Episode already complete. Call reset() first."}
}
self.step_count += 1
out, code = self.terminal.run(cmd)
penalty = -0.01
done = self.step_count >= self.limit
curr_score, task_info = self.task.eval(self.storage, self.terminal)
if curr_score > self.score:
bonus = (curr_score - self.score) * 0.5
penalty += bonus
self.score = curr_score
if curr_score >= 1.0 and not done:
penalty += 0.5
done = True
self.finished = True
if done and not self.finished:
self.finished = True
view = self._view()
return {
"observation": view,
"reward": penalty,
"done": done,
"info": {
"task_score": self.score,
"command": cmd,
"command_output": out,
"exit_code": code,
"output_length": len(out),
"step": self.step_count,
"max_steps": self.limit,
"task_metadata": task_info,
}
}
def _view(self) -> Dict[str, Any]:
pout, _ = self.terminal.run("ps")
lout, _ = self.terminal.run("ls /home/user/scripts")
return {
"current_directory": self.terminal.cwd,
"processes": pout,
"filesystem": lout,
"task_name": self.task.nm,
"task_description": self.task.desc,
"request": "Use commands to complete the task. Type your command below."
}
def dump(self) -> Dict:
snap = self.storage.snapshot()
return {
"filesystem": snap,
"episode_step": self.step_count,
"max_steps": self.limit,
"task_score": self.score,
"task_difficulty": self.difficulty,
"scenario_key": self._scenario_key,
"command_history": self.terminal.history(),
}
# ------------------------------------------------------------------
# Class-level helpers
# ------------------------------------------------------------------
@staticmethod
def avail_tasks() -> List[str]:
return all_task_keys()
@staticmethod
def task_details(key: str) -> Dict[str, Any]:
try:
return task_metadata(key)
except ValueError:
return {}
@staticmethod
def avail_scenarios() -> Dict[str, Dict]:
return list_scenarios()