File size: 6,053 Bytes
ce75dcf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""
FairRecovery β€” Constants and Configuration.

All configurable values are centralised here.
No hardcoded magic numbers anywhere else in the codebase.
"""

from __future__ import annotations

from enum import Enum, unique
from typing import Final


# ──────────────────────────────────────────────────────────────────────────────
# Environment Metadata
# ──────────────────────────────────────────────────────────────────────────────
ENV_NAME: Final[str] = "fairrecovery"
ENV_VERSION: Final[str] = "1.0.0"
ENV_DESCRIPTION: Final[str] = (
    "A post-disaster city recovery RL environment where an LLM agent must "
    "allocate limited resources across zones, optimising both efficiency and "
    "fairness for vulnerable populations. Designed for RLVR training via "
    "TRL/GRPO with Unsloth."
)

# ──────────────────────────────────────────────────────────────────────────────
# Episode Configuration
# ──────────────────────────────────────────────────────────────────────────────
MAX_DAYS: Final[int] = 5          # episode length (days)
MAX_ACTIONS_PER_DAY: Final[int] = 3   # max allocations per execute step
MAX_STEPS_SAFETY_CAP: Final[int] = 50  # hard cap on total steps to prevent infinite loops
DEFAULT_SEED: Final[int] = 42

# ──────────────────────────────────────────────────────────────────────────────
# Resource Definitions
# ──────────────────────────────────────────────────────────────────────────────
RESOURCE_COSTS: Final[dict] = {
    "power":   10,
    "water":   15,
    "medical": 20,
}

RESOURCE_EFFECTS: Final[dict] = {
    "power":   {"service": 0.20, "damage": -0.10},
    "water":   {"service": 0.30, "damage": -0.15},
    "medical": {"service": 0.40, "damage": -0.20},
}

# ──────────────────────────────────────────────────────────────────────────────
# Reward Weights
# ──────────────────────────────────────────────────────────────────────────────
REWARD_WEIGHTS: Final[dict] = {
    "exec": 1.0,   # service improvement
    "fair": 0.7,   # fairness (disparity reduction)
    "safe": 0.3,   # constraint satisfaction
}

# ──────────────────────────────────────────────────────────────────────────────
# Penalties
# ──────────────────────────────────────────────────────────────────────────────
PENALTY_INVALID_ZONE: Final[float] = -0.3
PENALTY_INVALID_RESOURCE: Final[float] = -0.3
PENALTY_BUDGET_EXCEEDED: Final[float] = -0.2
PENALTY_IGNORE_VULNERABLE: Final[float] = -0.3
PENALTY_WRONG_STAGE: Final[float] = -0.1
PENALTY_REPEATED_ACTION: Final[float] = -0.05

# ──────────────────────────────────────────────────────────────────────────────
# Thresholds
# ──────────────────────────────────────────────────────────────────────────────
VULNERABILITY_THRESHOLD: Final[float] = 0.6  # zones above this are "vulnerable"
SPAN_OVERLAP_THRESHOLD: Final[float] = 0.3

# Grader score bounds β€” strict open interval (never exactly 0 or 1)
GRADER_SCORE_MIN: Final[float] = 0.01
GRADER_SCORE_MAX: Final[float] = 0.99


# ──────────────────────────────────────────────────────────────────────────────
# Enums
# ──────────────────────────────────────────────────────────────────────────────
@unique
class Difficulty(str, Enum):
    """Task difficulty levels."""
    EASY   = "easy"
    MEDIUM = "medium"
    HARD   = "hard"


@unique
class ResourceType(str, Enum):
    """Available disaster-recovery resources."""
    POWER   = "power"
    WATER   = "water"
    MEDICAL = "medical"


@unique
class ActionType(str, Enum):
    """
    Agent action types β€” multi-step protocol:
      analyze  β†’ allocate β†’ execute  β†’ (repeat MAX_DAYS times) β†’ submit
    """
    ANALYZE  = "analyze"
    ALLOCATE = "allocate"
    EXECUTE  = "execute"
    SUBMIT   = "submit"
    NOOP     = "noop"


# Stage ordering for protocol enforcement
STAGE_SEQUENCE: Final[list] = [
    ActionType.ANALYZE,
    ActionType.ALLOCATE,
    ActionType.EXECUTE,
]