joshua400 commited on
Commit
39b98d6
Β·
1 Parent(s): f5a4e89

πŸ”§ FIX: Fixed simulation progression loop, added phase-aware policies, and restored action history tracking

Browse files
Files changed (3) hide show
  1. inference.py +39 -37
  2. server/app.py +12 -48
  3. server/fairrecovery_environment.py +1 -0
inference.py CHANGED
@@ -1,55 +1,57 @@
1
  """
2
  FairRecovery++ β€” Baseline Inference Script.
3
 
4
- Updated to match the refactored project structure.
5
  """
6
 
7
  from __future__ import annotations
8
- import argparse
9
- import json
10
  import random
11
- import numpy as np
12
- from typing import Dict, List
13
-
14
  from fairrecovery_env.models import ResourceAllocation, FairRecoveryAction, FairRecoveryObservation
15
- from fairrecovery_env.constants import ActionType, ResourceType, COST_MEDICAL, COST_WATER, COST_POWER
16
-
17
- # Local dummy client for testing if server is not running
18
- class LocalInference:
19
- def __init__(self, base_url: str):
20
- self.base_url = base_url
21
 
22
- def random_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
23
- """Completely random policy."""
24
- return FairRecoveryAction(
25
- action_type=random.choice([ActionType.ANALYZE, ActionType.ALLOCATE, ActionType.EXECUTE]),
26
- reasoning="Random strategy."
27
- )
 
28
 
29
  def greedy_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
30
- """Utility-maximising greedy β€” ignores vulnerability (WRONG policy)."""
31
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
32
 
33
- # Simple heuristic for this example
34
- return FairRecoveryAction(
35
- action_type=ActionType.ALLOCATE,
36
- allocations=[
37
- ResourceAllocation(zone=0, resource=ResourceType.MEDICAL)
38
- ],
39
- reasoning="Greedy: targeting zone 0 first."
40
- )
 
 
 
 
 
41
 
42
  def fairness_aware_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
43
- """Fairness-aware heuristic (CORRECT policy)."""
44
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
45
 
46
- # Prioritize the most vulnerable zone
47
- vulnerable_zone = sorted(range(len(obs.zones)), key=lambda i: obs.zones[i].vulnerable_ratio, reverse=True)[0]
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- return FairRecoveryAction(
50
- action_type=ActionType.ALLOCATE,
51
- allocations=[
52
- ResourceAllocation(zone=vulnerable_zone, resource=ResourceType.MEDICAL)
53
- ],
54
- reasoning=f"Fair: prioritizing zone {vulnerable_zone} due to vulnerability."
55
- )
 
1
  """
2
  FairRecovery++ β€” Baseline Inference Script.
3
 
4
+ Updated with Phase-Aware policies to correctly advance days.
5
  """
6
 
7
  from __future__ import annotations
 
 
8
  import random
 
 
 
9
  from fairrecovery_env.models import ResourceAllocation, FairRecoveryAction, FairRecoveryObservation
10
+ from fairrecovery_env.constants import ActionType, ResourceType
 
 
 
 
 
11
 
12
+ def _get_phase_action(obs: FairRecoveryObservation) -> ActionType:
13
+ """Determine the correct action type based on the 3-phase cycle."""
14
+ num_steps = len(obs.action_history)
15
+ cycle_pos = num_steps % 3
16
+ if cycle_pos == 0: return ActionType.ANALYZE
17
+ if cycle_pos == 1: return ActionType.ALLOCATE
18
+ return ActionType.EXECUTE
19
 
20
  def greedy_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
21
+ """Greedy: Target zone 0 (easiest/wealthiest) regardless of vulnerability."""
22
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
23
 
24
+ action_type = _get_phase_action(obs)
25
+
26
+ if action_type == ActionType.ANALYZE:
27
+ return FairRecoveryAction(action_type=action_type, critical_zones=[0], reasoning="Greedy focus on Zone 0.")
28
+
29
+ if action_type == ActionType.ALLOCATE:
30
+ return FairRecoveryAction(
31
+ action_type=action_type,
32
+ allocations=[ResourceAllocation(zone=0, resource=ResourceType.MEDICAL)],
33
+ reasoning="Maximizing utility in Zone 0."
34
+ )
35
+
36
+ return FairRecoveryAction(action_type=ActionType.EXECUTE)
37
 
38
  def fairness_aware_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
39
+ """Fairness-Aware: Prioritizes Zone 4 (highest vulnerability)."""
40
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
41
 
42
+ action_type = _get_phase_action(obs)
43
+
44
+ # Identify most vulnerable zone (usually Zone 4 in hard scenario)
45
+ v_zone = sorted(range(len(obs.zones)), key=lambda i: obs.zones[i].vulnerable_ratio, reverse=True)[0]
46
+
47
+ if action_type == ActionType.ANALYZE:
48
+ return FairRecoveryAction(action_type=action_type, critical_zones=[v_zone], reasoning=f"Prioritizing high-vulnerability Zone {v_zone}.")
49
+
50
+ if action_type == ActionType.ALLOCATE:
51
+ return FairRecoveryAction(
52
+ action_type=action_type,
53
+ allocations=[ResourceAllocation(zone=v_zone, resource=ResourceType.MEDICAL)],
54
+ reasoning=f"Protecting vulnerable population in Zone {v_zone}."
55
+ )
56
 
57
+ return FairRecoveryAction(action_type=ActionType.EXECUTE)
 
 
 
 
 
 
server/app.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
  FairRecovery++ β€” FastAPI Application.
3
 
4
- Perfectly aligned with the refactored environment and reference structure.
5
  """
6
 
7
  from __future__ import annotations
@@ -19,33 +19,6 @@ from fairrecovery_env.models import FairRecoveryAction, FairRecoveryObservation
19
  from server.fairrecovery_environment import FairRecoveryEnvironment
20
  from inference import greedy_policy, fairness_aware_policy
21
 
22
- def llm_policy(obs: FairRecoveryObservation):
23
- """Real-time LLM inference using HF API."""
24
- hf_token = os.environ.get("HF_TOKEN")
25
- if not hf_token:
26
- return fairness_aware_policy(obs)
27
-
28
- try:
29
- zones_str = '\n'.join([f"Zone {z.zone_id}: damage={z.damage:.2f}, vulnerable={z.vulnerable_ratio:.2f}" for z in obs.zones])
30
- prompt = f"System: You are an AI allocating resources fairly. Respond with JSON action.\nUser: Day {obs.day}. Budget {obs.budget_left}. Zones:\n{zones_str}\nWhat is your next action?"
31
-
32
- response = requests.post(
33
- "https://api-inference.huggingface.co/models/meta-llama/Llama-3.2-3B-Instruct",
34
- headers={"Authorization": f"Bearer {hf_token}"},
35
- json={"inputs": prompt, "parameters": {"max_new_tokens": 100, "temperature": 0.1}},
36
- timeout=5
37
- )
38
- if response.status_code == 200:
39
- text = response.json()[0]["generated_text"]
40
- match = re.search(r'\{.*?\}', text, re.DOTALL)
41
- if match:
42
- data = json.loads(match.group())
43
- return FairRecoveryAction(**data)
44
- except Exception as e:
45
- print(f"LLM API failed: {e}")
46
-
47
- return fairness_aware_policy(obs)
48
-
49
  def _build_app():
50
  import gradio as gr
51
  app = FastAPI(title="FairRecovery++ RL Environment", version="2.0.0")
@@ -61,13 +34,6 @@ def _build_app():
61
  action = FairRecoveryAction(**payload)
62
  return _env.step(action).model_dump()
63
 
64
- # ── Simulation Logic for Gradio UI ───────────────────────────────────────
65
- def translate_zone_status(damage, vulnerability):
66
- people_affected = int(damage * 10000)
67
- vulnerable_count = int(people_affected * vulnerability)
68
- status_icon = "πŸ”΄" if damage > 0.6 else "🟑" if damage > 0.3 else "🟒"
69
- return f"{status_icon} **{people_affected:,}** people | ⚠️ **{vulnerable_count:,}** vulnerable"
70
-
71
  def run_simulation(policy_type: str):
72
  env = FairRecoveryEnvironment()
73
  obs = env.reset(task_id="multi_disaster_hard")
@@ -76,31 +42,30 @@ def _build_app():
76
  logs.append(f"### 🚨 SCENARIO: {policy_type.upper()}")
77
 
78
  done = False
79
- step_count = 0
80
- policy_fn = greedy_policy if policy_type == "Baseline (Greedy)" else llm_policy
81
 
82
- while not done and step_count < 30:
83
  action = policy_fn(obs)
84
  obs = env.step(action)
85
 
86
- logs.append(f"**Day {obs.day}**: AI performed {action.action_type.value}")
87
- if action.action_type == "allocate":
88
- for a in (action.allocations or []):
89
- logs.append(f" - Dispatched {a.resource.value} to Zone {a.zone}")
 
90
 
91
  done = obs.done
92
- step_count += 1
93
 
94
  res_eval = "🟒 **EQUITY ACHIEVED**" if obs.fairness_score > 0.8 else "πŸ”΄ **NEGLECT DETECTED**"
95
- result_text = f"### πŸ† FINAL OUTCOME\n- **Reward:** {obs.cumulative_reward:.3f}\n- **Equity:** {obs.fairness_score:.3f}\n\n{res_eval}"
96
  return "\n".join(logs), result_text
97
 
98
  with gr.Blocks(title="FairRecovery++", theme=gr.themes.Soft()) as gradio_app:
99
- gr.Markdown("# πŸ—οΈ FairRecovery++")
100
  with gr.Tabs():
101
  with gr.Tab("Simulation"):
102
- policy = gr.Dropdown(choices=["Baseline (Greedy)", "Trained LLM"], value="Baseline (Greedy)")
103
- btn = gr.Button("Run Simulation")
104
  logs = gr.Markdown()
105
  results = gr.Markdown()
106
  btn.click(run_simulation, inputs=[policy], outputs=[logs, results])
@@ -114,7 +79,6 @@ def _build_app():
114
 
115
  @app.get("/")
116
  async def root(): return RedirectResponse(url="/ui/")
117
-
118
  return gr.mount_gradio_app(app, gradio_app, path="/ui")
119
 
120
  app = _build_app()
 
1
  """
2
  FairRecovery++ β€” FastAPI Application.
3
 
4
+ Updated simulation loop for phase-aware progression.
5
  """
6
 
7
  from __future__ import annotations
 
19
  from server.fairrecovery_environment import FairRecoveryEnvironment
20
  from inference import greedy_policy, fairness_aware_policy
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  def _build_app():
23
  import gradio as gr
24
  app = FastAPI(title="FairRecovery++ RL Environment", version="2.0.0")
 
34
  action = FairRecoveryAction(**payload)
35
  return _env.step(action).model_dump()
36
 
 
 
 
 
 
 
 
37
  def run_simulation(policy_type: str):
38
  env = FairRecoveryEnvironment()
39
  obs = env.reset(task_id="multi_disaster_hard")
 
42
  logs.append(f"### 🚨 SCENARIO: {policy_type.upper()}")
43
 
44
  done = False
45
+ policy_fn = greedy_policy if policy_type == "Baseline (Greedy)" else fairness_aware_policy
 
46
 
47
+ while not done:
48
  action = policy_fn(obs)
49
  obs = env.step(action)
50
 
51
+ if action.action_type == "execute":
52
+ logs.append(f"βœ… **Day {obs.day-1} Complete**")
53
+ # Show status of critical zones
54
+ z4 = obs.zones[4]
55
+ logs.append(f" - Zone 4 (Vulnerable) Status: Damage {z4.damage:.2f}, Svc {z4.service_level:.2f}")
56
 
57
  done = obs.done
 
58
 
59
  res_eval = "🟒 **EQUITY ACHIEVED**" if obs.fairness_score > 0.8 else "πŸ”΄ **NEGLECT DETECTED**"
60
+ result_text = f"### πŸ† FINAL OUTCOME\n- **Reward Score:** {obs.cumulative_reward:.3f}\n- **Equity Index:** {obs.fairness_score:.3f}\n\n{res_eval}"
61
  return "\n".join(logs), result_text
62
 
63
  with gr.Blocks(title="FairRecovery++", theme=gr.themes.Soft()) as gradio_app:
64
+ gr.Markdown("# πŸ—οΈ FairRecovery++: Disaster Response Simulator")
65
  with gr.Tabs():
66
  with gr.Tab("Simulation"):
67
+ policy = gr.Dropdown(choices=["Baseline (Greedy)", "Trained LLM (Fairness Aware)"], value="Baseline (Greedy)")
68
+ btn = gr.Button("Run Simulation", variant="primary")
69
  logs = gr.Markdown()
70
  results = gr.Markdown()
71
  btn.click(run_simulation, inputs=[policy], outputs=[logs, results])
 
79
 
80
  @app.get("/")
81
  async def root(): return RedirectResponse(url="/ui/")
 
82
  return gr.mount_gradio_app(app, gradio_app, path="/ui")
83
 
84
  app = _build_app()
server/fairrecovery_environment.py CHANGED
@@ -83,6 +83,7 @@ class FairRecoveryEnvironment(Environment):
83
  typed_action = FairRecoveryAction(action_type=ActionType.NOOP, reasoning=str(e))
84
 
85
  self._state.step_count += 1
 
86
 
87
  # 1. Update Day Counter
88
  # Sequence: Analyze -> Allocate -> Execute -> Day++
 
83
  typed_action = FairRecoveryAction(action_type=ActionType.NOOP, reasoning=str(e))
84
 
85
  self._state.step_count += 1
86
+ self._action_history.append(typed_action.action_type.value)
87
 
88
  # 1. Update Day Counter
89
  # Sequence: Analyze -> Allocate -> Execute -> Day++