CoolFace
Apppublic

Deva22467/govt-scheme-openenv

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
env.py127 linesDownload Raw Back to root
1import random2import numpy as np3from typing import Dict, Any, Tuple, List4from data import PROFILES, SCHEMES_DB, get_profiles_by_task5from models import Observation, Action6 7class SchemeEnv:8    """9    OpenEnv Reinforcement Learning Multi-Step trajectory environment for Government Scheme Recommendation.10    Models decision-making used by digital governance assistants to improve scheme adoption.11    """12    def __init__(self, seed: int = 42):13        self.profiles = PROFILES14        self.current_profile: Dict[str, Any] = {}15        self.task_mode = "medium"16        self.stage = 017        self.episode_reward = 0.018        self.seed(seed)19 20    def seed(self, seed: int):21        """Sets the seed for reproducible deterministic states."""22        random.seed(seed)23        np.random.seed(seed)24 25    def reset(self, task: str = None, profile_id: str = None) -> Observation:26        """Resets the environment."""27        self.stage = 028        self.episode_reward = 0.029        30        if profile_id:31            candidates = [p for p in self.profiles if p['id'] == profile_id]32            self.current_profile = candidates[0] if candidates else random.choice(self.profiles)33        elif task:34            candidates = get_profiles_by_task(task)35            self.current_profile = random.choice(candidates) if candidates else random.choice(self.profiles)36        else:37            self.current_profile = random.choice(self.profiles)38            39        self.task_mode = self.current_profile['task']40        return self.state()41 42    def state(self) -> Observation:43        """Returns Observation class including dynamic noise."""44        if not self.current_profile:45            # Fallback initialization bounding46            return Observation(age=0, income=0, category="", occupation="", state="", task="", stage=0)47        48        state_dict = self.current_profile['profile'].copy()49        50        if self.task_mode == "hard":51            if "occupation" in state_dict:52                noise_postfix = random.choice([" + part time freelancer", " / informal gig worker", " + hobbyist"])53                state_dict["occupation"] = state_dict["occupation"] + noise_postfix54                55            irrelevant_noise = [56                {"has_pets": random.choice([True, False])},57                {"favorite_color": random.choice(["Blue", "Green", "Red"])},58                {"zodiac_sign": random.choice(["Libra", "Aries", "Taurus", "Gemini"])}59            ]60            state_dict.update(random.choice(irrelevant_noise))61 62        state_dict["task"] = self.task_mode63        state_dict["stage"] = self.stage64        return Observation(**state_dict)65 66    def step(self, action: Action) -> Tuple[Observation, float, bool, Dict]:67        """Trajectory Step. Receives structured Action Schema Pydantic."""68        if not self.current_profile:69            raise ValueError("Environment must be reset.")70 71        gt_schemes = set(self.current_profile['eligible_schemes'])72        gt_best = self.current_profile['best_scheme']73        gt_reason = self.current_profile['reasoning']74        75        step_reward = 0.076        info = {77            "stage": self.stage,78            "task": self.task_mode,79            "eligible_score": 0.0,80            "best_score": 0.0,81            "reason_score": 0.0,82            "total_reward": 0.083        }84        85        if self.stage == 0:86            pred_schemes = set(action.schemes) if action.schemes else set()87            correct_schemes = gt_schemes.intersection(pred_schemes)88            89            if len(gt_schemes) > 0:90                fraction = len(correct_schemes) / len(gt_schemes)91                step_reward += fraction * 0.392            info["eligible_score"] = step_reward93            94        elif self.stage == 1:95            pred_best = action.best if action.best else ''96            if pred_best == gt_best:97                step_reward += 0.498                99            if pred_best in gt_schemes:100                benefit_values = [SCHEMES_DB.get(s, {}).get("benefit_amount", 0) for s in gt_schemes]101                if benefit_values:102                    max_benefit = max(benefit_values)103                    pred_benefit = SCHEMES_DB.get(pred_best, {}).get("benefit_amount", 0)104                    if max_benefit > 0 and pred_benefit == max_benefit:105                        step_reward += 0.2106            info["best_score"] = step_reward107            108        elif self.stage == 2:109            pred_reason = action.reasoning if action.reasoning else ''110            if pred_reason and isinstance(pred_reason, str) and len(pred_reason.split()) >= 3:111                gt_words = set(gt_reason.lower().split())112                pred_words = set(pred_reason.lower().split())113                if len(gt_words.intersection(pred_words)) >= 2:114                    step_reward += 0.3115            info["reason_score"] = step_reward116        117        allowed_reward = 1.0 - self.episode_reward118        yield_reward = min(step_reward, allowed_reward)119        self.episode_reward += yield_reward120        self.stage += 1121        done = (self.stage >= 3)122        info["total_reward"] = self.episode_reward123        124        yield_reward = max(0.05, min(yield_reward, 0.95))125        126        return self.state(), yield_reward, done, info127