my-env / server /scam_openenv_environment.py
Maheswar01's picture
feat: six canonical tasks, task_registry, DaddyCoder-style graders layout
34a3f52
Raw
History Blame Contribute Delete
4.07 kB
"""OpenEnv Environment adapter wrapping in-repo ScamEnv."""
from __future__ import annotations
import os
import sys
from pathlib import Path
from typing import Any, Optional
from uuid import uuid4
ROOT = Path(__file__).resolve().parent.parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from openenv.core.env_server.interfaces import Environment
from openenv.core.env_server.types import EnvironmentMetadata
from env.scam_env import ScamEnv
from server.openenv_types import ScamEnvAction, ScamEnvObservation, ScamEnvState
def _task_max_steps(task: str) -> int:
from tasks.task_registry import MAX_STEPS_BY_TASK
return MAX_STEPS_BY_TASK.get(task, MAX_STEPS_BY_TASK["single_turn_triage"])
class ScamOpenEnvEnvironment(Environment[ScamEnvAction, ScamEnvObservation, ScamEnvState]):
"""HTTP-facing environment; delegates to ScamEnv."""
SUPPORTS_CONCURRENT_SESSIONS = True
def __init__(self) -> None:
super().__init__(transform=None, rubric=None)
self._task = os.getenv("SCAM_ENV_TASK", "easy")
self._core = ScamEnv(task_id=self._task, max_steps=_task_max_steps(self._task))
self._state = ScamEnvState(episode_id=str(uuid4()), step_count=0, task_id=self._task)
self._last_scenario_id: str | None = None
def reset(
self,
seed: Optional[int] = None,
episode_id: Optional[str] = None,
**kwargs: Any,
) -> ScamEnvObservation:
self._reset_rubric()
scenario_id = kwargs.get("scenario_id")
obs, info = self._core.reset(seed=seed, scenario_id=scenario_id)
self._last_scenario_id = info["scenario_id"]
self._state = ScamEnvState(
episode_id=episode_id or self._last_scenario_id or str(uuid4()),
step_count=0,
task_id=self._task,
action_trace=list(self._core.action_trace),
)
return self._wrap_obs(obs, reward=0.0, done=False)
def step(
self,
action: ScamEnvAction,
timeout_s: Optional[float] = None,
**kwargs: Any,
) -> ScamEnvObservation:
obs, reward, done, _info = self._core.step(action.action)
self._state.step_count = len(self._core.action_trace)
self._state.action_trace = list(self._core.action_trace)
return self._wrap_obs(obs, reward=float(reward), done=bool(done))
@property
def state(self) -> ScamEnvState:
self._state.action_trace = list(self._core.action_trace)
self._state.step_count = len(self._core.action_trace)
return self._state
def get_metadata(self) -> EnvironmentMetadata:
return EnvironmentMetadata(
name="scam-detection-env",
description="AI scam detection & response training simulation",
version="1.0.0",
)
def close(self) -> None:
self._core.close()
def _wrap_obs(self, obs: dict[str, Any], *, reward: float, done: bool) -> ScamEnvObservation:
return ScamEnvObservation(
done=done,
reward=reward,
metadata={},
observation_schema_version=obs.get("observation_schema_version", "1.1"),
case_id=obs.get("case_id", ""),
message_text=obs.get("message_text", ""),
sender_type=obs.get("sender_type", "unknown"),
channel=obs.get("channel", "sms"),
link_present=bool(obs.get("link_present", False)),
urgency_score=float(obs.get("urgency_score", 0.0)),
conversation_history=list(obs.get("conversation_history") or []),
sender_verified=obs.get("sender_verified"),
risk_score=float(obs.get("risk_score", 0.0)),
risk_factors=list(obs.get("risk_factors") or []),
steps_taken=int(obs.get("steps_taken", 0)),
max_episode_steps=int(obs.get("max_episode_steps", 20)),
terminal_actions=tuple(obs.get("terminal_actions") or ("ignore", "flag_scam", "block_sender", "escalate_to_bank")),
scenario_id=obs.get("case_id", ""),
)