Spaces:
Sleeping
Sleeping
| import json | |
| import random | |
| import uuid | |
| from pathlib import Path | |
| from typing import Any, List, Optional | |
| from openenv.core.env_server.interfaces import Environment | |
| from models import SFDAction, SFDObservation, SFDState | |
| from server.grader import compute_reward | |
| DATASET_PATH = Path(__file__).parent.parent / "data" / "dataset.json" | |
| TASK_CONFIG = { | |
| "easy": {"n": 10, "difficulty": "easy"}, | |
| "medium": {"n": 20, "difficulty": "medium"}, | |
| "hard": {"n": 30, "difficulty": "hard"}, | |
| } | |
| class SilentFailureEnvironment(Environment[SFDAction, SFDObservation, SFDState]): | |
| SUPPORTS_CONCURRENT_SESSIONS = True | |
| def __init__(self, **kwargs: Any): | |
| super().__init__(**kwargs) | |
| self._dataset: List[dict] = json.loads(DATASET_PATH.read_text()) | |
| self._items: List[dict] = [] | |
| self._task: str = "easy" | |
| self._step: int = 0 | |
| self._session_id: str = "" | |
| self._flags: List[Optional[bool]] = [] | |
| self._confidences: List[Optional[float]] = [] | |
| def reset( | |
| self, | |
| seed: Optional[int] = None, | |
| episode_id: Optional[str] = None, | |
| task: str = "easy", | |
| **kwargs: Any, | |
| ) -> SFDObservation: | |
| if seed is not None: | |
| random.seed(seed) | |
| cfg = TASK_CONFIG.get(task, TASK_CONFIG["easy"]) | |
| pool = [x for x in self._dataset if x["difficulty"] == cfg["difficulty"]] | |
| n = min(cfg["n"], len(pool)) | |
| self._items = random.sample(pool, n) | |
| self._task = task | |
| self._step = 0 | |
| self._session_id = episode_id or str(uuid.uuid4()) | |
| self._flags = [None] * len(self._items) | |
| self._confidences = [None] * len(self._items) | |
| return self._make_obs() | |
| def step( | |
| self, | |
| action: SFDAction, | |
| timeout_s: Optional[float] = None, | |
| **kwargs: Any, | |
| ) -> SFDObservation: | |
| if self._step >= len(self._items): | |
| obs = self._terminal_obs() | |
| obs.done = True | |
| return obs | |
| flag, confidence = self._parse_message(action.message) | |
| self._flags[self._step] = flag | |
| self._confidences[self._step] = confidence | |
| self._step += 1 | |
| done = self._step >= len(self._items) | |
| reward = 0.0 | |
| if done: | |
| reward = compute_reward( | |
| [item["label"] for item in self._items], | |
| self._flags, | |
| self._confidences, | |
| ) | |
| obs = self._terminal_obs() if done else self._make_obs() | |
| obs.done = done | |
| obs.reward = reward | |
| return obs | |
| def state(self) -> SFDState: | |
| return SFDState( | |
| episode_id=self._session_id, | |
| step_count=self._step, | |
| task=self._task, | |
| flags=self._flags, | |
| confidences=self._confidences, | |
| ) | |
| def _parse_message(self, message: str): | |
| try: | |
| clean = message.strip().strip("```json").strip("```").strip() | |
| data = json.loads(clean) | |
| flag = bool(data.get("flag", False)) | |
| conf = float(data.get("confidence", 0.5)) | |
| return flag, max(0.0, min(1.0, conf)) | |
| except Exception: | |
| return False, 0.5 | |
| def _make_obs(self) -> SFDObservation: | |
| item = self._items[self._step] | |
| msg = ( | |
| f"Item {self._step + 1} of {len(self._items)} | Domain: {item['domain']}\n\n" | |
| f"QUESTION: {item['question']}\n\n" | |
| f"AI RESPONSE: {item['ai_response']}\n\n" | |
| "Determine if this AI response is CONFIDENTLY WRONG (a silent failure).\n" | |
| 'Respond with JSON only: {"flag": true/false, "confidence": 0.0-1.0}\n' | |
| " flag=true → factually wrong AND stated without hedging\n" | |
| " flag=false → correct, uncertain/hedged, or misleading-but-not-confidently-wrong\n" | |
| " confidence → your certainty in this classification" | |
| ) | |
| return SFDObservation( | |
| session_id=self._session_id, | |
| step=self._step, | |
| total_steps=len(self._items), | |
| item_id=item["id"], | |
| domain=item["domain"], | |
| difficulty=item["difficulty"], | |
| question=item["question"], | |
| ai_response=item["ai_response"], | |
| message=msg, | |
| echoed_message=msg, | |
| done=False, | |
| reward=None, | |
| ) | |
| def _terminal_obs(self) -> SFDObservation: | |
| msg = f"Episode complete. {len(self._items)} items classified." | |
| return SFDObservation( | |
| session_id=self._session_id, | |
| step=self._step, | |
| total_steps=len(self._items), | |
| item_id="__terminal__", | |
| domain="", | |
| difficulty=self._task, | |
| question="", | |
| ai_response="", | |
| message=msg, | |
| echoed_message=msg, | |
| done=True, | |
| reward=None, | |
| ) |