neurohack-eval-env / server /environment.py
abrar6024's picture
Upload server/environment.py with huggingface_hub
f799bb7 verified
Raw
History Blame Contribute Delete
7.15 kB
"""
Task 1: Prompt Sensitivity Detection — stateless design for HF Spaces
The scenario is embedded in the observation so step() works across replicas.
"""
from __future__ import annotations
import random, sys, os, json
sys.path.insert(0, os.path.dirname(__file__))
from typing import Optional, List
from openenv.core import Environment, Action, Observation, State
from grader import grade_sensitivity
SCENARIOS = [
{"id":0,"base_prompt":"Summarize the benefits of exercise.","variants":["Summarize the benefits of exercise.","List the benefits of exercise.","What are the advantages of working out?"],"ai_responses":["Exercise improves cardiovascular health, boosts mood, and increases energy.","1. Better heart health 2. Improved mood 3. More energy","Working out can help you lose weight and feel happier."],"ground_truth":"sensitive","sensitive_index":2,"gt_explanation":"Third variant shifts focus to weight loss."},
{"id":1,"base_prompt":"What is the capital of France?","variants":["What is the capital of France?","Tell me the capital city of France.","Name the capital of France."],"ai_responses":["Paris","The capital of France is Paris.","Paris is the capital of France."],"ground_truth":"stable","sensitive_index":-1,"gt_explanation":"All variants say Paris."},
{"id":2,"base_prompt":"Explain recursion in programming.","variants":["Explain recursion in programming.","What is recursion in coding?","Describe recursion for a 5-year-old."],"ai_responses":["Recursion is when a function calls itself to solve a smaller version of the same problem.","Recursion: a function that calls itself until a base case is met.","Imagine a box inside a box inside a box, that is recursion!"],"ground_truth":"sensitive","sensitive_index":2,"gt_explanation":"Third variant uses childlike analogy not technical definition."},
{"id":3,"base_prompt":"How do I sort a list in Python?","variants":["How do I sort a list in Python?","What is the way to sort a list in Python?","Give me Python code to sort a list."],"ai_responses":["Use list.sort() for in-place sorting or sorted(list) to return a new sorted list.","You can sort a Python list using list.sort() or sorted().","my_list=[3,1,2]; my_list.sort(); print(my_list)"],"ground_truth":"sensitive","sensitive_index":2,"gt_explanation":"Third variant gives code only with no explanation."},
{"id":4,"base_prompt":"Who wrote Hamlet?","variants":["Who wrote Hamlet?","Who is the author of Hamlet?","Which playwright wrote Hamlet?"],"ai_responses":["William Shakespeare wrote Hamlet.","The author of Hamlet is William Shakespeare.","Hamlet was written by William Shakespeare."],"ground_truth":"stable","sensitive_index":-1,"gt_explanation":"All variants credit Shakespeare."},
{"id":5,"base_prompt":"What is machine learning?","variants":["What is machine learning?","Define machine learning.","Explain machine learning like I am a CEO."],"ai_responses":["Machine learning is a subset of AI where systems learn patterns from data.","Machine learning: algorithms that improve through experience.","ML lets software get smarter over time by analyzing data, giving you a competitive edge."],"ground_truth":"sensitive","sensitive_index":2,"gt_explanation":"Third variant produces a business-framing response."},
{"id":6,"base_prompt":"What is the boiling point of water?","variants":["What is the boiling point of water?","At what temperature does water boil?","When does water start boiling?"],"ai_responses":["Water boils at 100C (212F) at standard atmospheric pressure.","Water boils at 100 Celsius or 212 Fahrenheit.","Water starts boiling at 100C or 212F under normal conditions."],"ground_truth":"stable","sensitive_index":-1,"gt_explanation":"All variants agree on 100C / 212F."},
{"id":7,"base_prompt":"How do I reverse a string in Python?","variants":["How do I reverse a string in Python?","What is the Python way to reverse a string?","Reverse a string in Python, give me a one-liner."],"ai_responses":["You can reverse a string using slicing: my_string[::-1] or reversed().","In Python, string reversal is done with slicing: s[::-1].","s[::-1]"],"ground_truth":"sensitive","sensitive_index":2,"gt_explanation":"Third variant returns only a one-liner with zero explanation."},
]
SCENARIO_MAP = {s["id"]: s for s in SCENARIOS}
class SensitivityAction(Action):
verdict: str
confidence: float
explanation: str
scenario_id: int = 0 # echoed back from observation
sensitive_variant_index: Optional[int] = None
class SensitivityObservation(Observation):
task: str
level: str
episode_id: int
scenario_id: int # key for stateless step()
base_prompt: str
variants: List[str]
ai_responses: List[str]
instruction: str
class PromptSensitivityEnvironment(Environment):
def __init__(self):
self._episode_id = 0
self._done = False
self._step_count = 0
def reset(self, **kwargs) -> SensitivityObservation:
scenario = random.choice(SCENARIOS)
self._episode_id += 1
self._done = False
self._step_count = 0
self._current_scenario_id = scenario["id"]
return self._obs(scenario)
def step(self, action: SensitivityAction, **kwargs):
if self._done:
scenario = SCENARIO_MAP.get(action.scenario_id, SCENARIOS[0])
return self._obs(scenario), 0.0, True, {"error": "Episode done. Call reset()."}
# Stateless: look up scenario from action.scenario_id
scenario = SCENARIO_MAP.get(action.scenario_id, SCENARIO_MAP.get(self._current_scenario_id, SCENARIOS[0]))
self._step_count += 1
reward, info = grade_sensitivity(action, scenario)
self._done = True
info.update({
"episode_id": self._episode_id,
"step": self._step_count,
"ground_truth": scenario["ground_truth"],
"ground_truth_sensitive_index": scenario["sensitive_index"],
"ground_truth_explanation": scenario["gt_explanation"],
})
return self._obs(scenario), reward, self._done, info
def state(self) -> State:
return State()
def _obs(self, scenario) -> SensitivityObservation:
return SensitivityObservation(
task="prompt_sensitivity_detection",
level="easy",
episode_id=self._episode_id,
scenario_id=scenario["id"],
base_prompt=scenario["base_prompt"],
variants=scenario["variants"],
ai_responses=scenario["ai_responses"],
instruction=(
"Analyze the AI responses to the prompt variants. "
"Determine if the output is 'sensitive' (changes significantly), "
"'stable' (consistent), or 'partial' (minor differences). "
"If sensitive, set sensitive_variant_index to the variant index that caused inconsistency. "
"Include scenario_id from this observation unchanged in your action."
),
)