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

πŸš€ FEAT: Integrated Live HF LLM (Llama-3) & Training Data Logging

Browse files
Files changed (2) hide show
  1. inference.py +88 -34
  2. server/app.py +36 -20
inference.py CHANGED
@@ -1,16 +1,94 @@
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
@@ -18,40 +96,16 @@ def _get_phase_action(obs: FairRecoveryObservation) -> ActionType:
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)
 
1
  """
2
+ FairRecovery++ β€” Advanced Inference & LLM Connectivity.
3
 
4
+ Integrates Hugging Face Inference API for real-time decision making
5
+ and trajectory logging for training data generation.
6
  """
7
 
8
  from __future__ import annotations
9
+ import os
10
+ import json
11
+ import time
12
+ from typing import Optional, List
13
+ from huggingface_hub import InferenceClient
14
  from fairrecovery_env.models import ResourceAllocation, FairRecoveryAction, FairRecoveryObservation
15
  from fairrecovery_env.constants import ActionType, ResourceType
16
 
17
+ class TrainingLogger:
18
+ """Logs state-action trajectories for future RL training."""
19
+ def __init__(self, log_dir: str = "training_data"):
20
+ self.log_dir = log_dir
21
+ os.makedirs(log_dir, exist_ok=True)
22
+ self.current_session = f"session_{int(time.time())}.jsonl"
23
+
24
+ def log_step(self, obs: FairRecoveryObservation, action: FairRecoveryAction, reward: float):
25
+ entry = {
26
+ "observation": obs.model_dump(),
27
+ "action": action.model_dump(),
28
+ "reward": reward,
29
+ "timestamp": time.time()
30
+ }
31
+ with open(os.path.join(self.log_dir, self.current_session), "a") as f:
32
+ f.write(json.dumps(entry) + "\n")
33
+
34
+ class HFInferencePolicy:
35
+ """Live LLM Agent using Hugging Face Inference API."""
36
+ def __init__(self, model_id: str = "meta-llama/Llama-3.2-1B-Instruct", token: Optional[str] = None):
37
+ self.client = InferenceClient(model=model_id, token=token)
38
+
39
+ def __call__(self, obs: FairRecoveryObservation) -> FairRecoveryAction:
40
+ if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
41
+
42
+ prompt = self._build_prompt(obs)
43
+ try:
44
+ response = self.client.chat_completion(
45
+ messages=[{"role": "user", "content": prompt}],
46
+ max_tokens=200,
47
+ temperature=0.1
48
+ )
49
+ content = response.choices[0].message.content
50
+ return self._parse_response(content, obs)
51
+ except Exception as e:
52
+ # Fallback to a simple heuristic if API fails
53
+ return FairRecoveryAction(action_type=ActionType.ANALYZE, reasoning=f"API Error: {str(e)}")
54
+
55
+ def _build_prompt(self, obs: FairRecoveryObservation) -> str:
56
+ zones_info = "\n".join([f"Zone {z.zone_id}: Damage={z.damage:.2f}, Vulnerability={z.vulnerable_ratio:.2f}, Svc={z.service_level:.2f}" for z in obs.zones])
57
+ return f"""
58
+ You are an Emergency Recovery Agent.
59
+ Environment: {zones_info}
60
+ Day: {obs.day}
61
+ Budget Left: {obs.budget_left:.2f}
62
+
63
+ Goal: Maximize Utility AND Fairness.
64
+ Format your response as valid JSON:
65
+ {{"action_type": "analyze"|"allocate"|"execute", "zone": <int>, "reasoning": "<str>"}}
66
+
67
+ Choose "analyze" to scan a zone, "allocate" to send resources (MEDICAL to <zone>), or "execute" to finish the day.
68
+ """
69
+
70
+ def _parse_response(self, content: str, obs: FairRecoveryObservation) -> FairRecoveryAction:
71
+ try:
72
+ # Extract JSON from potential conversational filler
73
+ match = __import__("re").search(r"\{.*\}", content, __import__("re").DOTALL)
74
+ data = json.loads(match.group(0)) if match else json.loads(content)
75
+
76
+ a_type = ActionType(data["action_type"].lower())
77
+ allocs = None
78
+ if a_type == ActionType.ALLOCATE:
79
+ allocs = [ResourceAllocation(zone=data.get("zone", 0), resource=ResourceType.MEDICAL)]
80
+
81
+ return FairRecoveryAction(
82
+ action_type=a_type,
83
+ critical_zones=[data.get("zone", 0)] if a_type == ActionType.ANALYZE else None,
84
+ allocations=allocs,
85
+ reasoning=data.get("reasoning", "LLM decision.")
86
+ )
87
+ except:
88
+ return FairRecoveryAction(action_type=ActionType.EXECUTE, reasoning="Parse failed, auto-executing.")
89
+
90
+ # Standard heuristic policies for comparison
91
  def _get_phase_action(obs: FairRecoveryObservation) -> ActionType:
 
92
  num_steps = len(obs.action_history)
93
  cycle_pos = num_steps % 3
94
  if cycle_pos == 0: return ActionType.ANALYZE
 
96
  return ActionType.EXECUTE
97
 
98
  def greedy_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
 
99
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
100
+ a_type = _get_phase_action(obs)
101
+ if a_type == ActionType.ANALYZE: return FairRecoveryAction(action_type=a_type, critical_zones=[0])
102
+ if a_type == ActionType.ALLOCATE: return FairRecoveryAction(action_type=a_type, allocations=[ResourceAllocation(zone=0, resource=ResourceType.MEDICAL)])
 
 
 
 
 
 
 
 
 
 
103
  return FairRecoveryAction(action_type=ActionType.EXECUTE)
104
 
105
  def fairness_aware_policy(obs: FairRecoveryObservation) -> FairRecoveryAction:
 
106
  if obs.day > 10: return FairRecoveryAction(action_type=ActionType.SUBMIT)
107
+ a_type = _get_phase_action(obs)
 
 
 
108
  v_zone = sorted(range(len(obs.zones)), key=lambda i: obs.zones[i].vulnerable_ratio, reverse=True)[0]
109
+ if a_type == ActionType.ANALYZE: return FairRecoveryAction(action_type=a_type, critical_zones=[v_zone])
110
+ if a_type == ActionType.ALLOCATE: return FairRecoveryAction(action_type=a_type, allocations=[ResourceAllocation(zone=v_zone, resource=ResourceType.MEDICAL)])
 
 
 
 
 
 
 
 
 
111
  return FairRecoveryAction(action_type=ActionType.EXECUTE)
server/app.py CHANGED
@@ -1,28 +1,27 @@
1
  """
2
  FairRecovery++ β€” FastAPI Application.
3
 
4
- Updated simulation loop for phase-aware progression.
5
  """
6
 
7
  from __future__ import annotations
8
  import os, sys
9
  import re
10
- import requests
11
- import json
12
  from typing import Optional
13
  from fastapi import FastAPI, Request
14
  from fastapi.responses import RedirectResponse
15
 
16
  sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
17
 
18
- 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 _build_app():
23
  import gradio as gr
24
  app = FastAPI(title="FairRecovery++ RL Environment", version="2.0.0")
25
  _env = FairRecoveryEnvironment()
 
26
 
27
  @app.post("/reset")
28
  async def reset(difficulty: str = "medium", episode_id: Optional[str] = None):
@@ -34,48 +33,65 @@ def _build_app():
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")
40
 
41
  logs = []
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])
72
  with gr.Tab("README"):
73
  readme_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "README.md")
74
  with open(readme_path, "r", encoding="utf-8") as f:
75
- content = f.read()
76
- if content.startswith("---"):
77
- content = re.sub(r"^---.*?---", "", content, flags=re.DOTALL)
78
- gr.Markdown(content)
79
 
80
  @app.get("/")
81
  async def root(): return RedirectResponse(url="/ui/")
 
1
  """
2
  FairRecovery++ β€” FastAPI Application.
3
 
4
+ Updated with Live LLM (Hugging Face) support and Training Data logging.
5
  """
6
 
7
  from __future__ import annotations
8
  import os, sys
9
  import re
 
 
10
  from typing import Optional
11
  from fastapi import FastAPI, Request
12
  from fastapi.responses import RedirectResponse
13
 
14
  sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
15
 
16
+ from fairrecovery_env.models import FairRecoveryAction
17
  from server.fairrecovery_environment import FairRecoveryEnvironment
18
+ from inference import greedy_policy, fairness_aware_policy, HFInferencePolicy, TrainingLogger
19
 
20
  def _build_app():
21
  import gradio as gr
22
  app = FastAPI(title="FairRecovery++ RL Environment", version="2.0.0")
23
  _env = FairRecoveryEnvironment()
24
+ _logger = TrainingLogger()
25
 
26
  @app.post("/reset")
27
  async def reset(difficulty: str = "medium", episode_id: Optional[str] = None):
 
33
  action = FairRecoveryAction(**payload)
34
  return _env.step(action).model_dump()
35
 
36
+ def run_simulation(policy_type: str, hf_token: str):
37
  env = FairRecoveryEnvironment()
38
  obs = env.reset(task_id="multi_disaster_hard")
39
 
40
  logs = []
41
+ logs.append(f"### πŸš€ LIVE SESSION: {policy_type.upper()}")
42
 
43
+ # Policy Selection
44
+ if policy_type == "Live LLM (Llama-3)":
45
+ if not hf_token:
46
+ return "### ❌ Error\nPlease provide a Hugging Face Token to use the Live LLM.", ""
47
+ policy_fn = HFInferencePolicy(token=hf_token)
48
+ elif policy_type == "Baseline (Greedy)":
49
+ policy_fn = greedy_policy
50
+ else:
51
+ policy_fn = fairness_aware_policy
52
 
53
+ done = False
54
  while not done:
55
  action = policy_fn(obs)
56
+ prev_obs = obs # For logging
57
  obs = env.step(action)
58
 
59
+ # Log for training
60
+ _logger.log_step(prev_obs, action, obs.reward)
61
+
62
  if action.action_type == "execute":
63
  logs.append(f"βœ… **Day {obs.day-1} Complete**")
 
64
  z4 = obs.zones[4]
65
+ logs.append(f" - Zone 4 Status: Damage {z4.damage:.2f}, Equity Index: {obs.fairness_score:.2f}")
66
+ if action.reasoning:
67
+ logs.append(f" - *AI Reasoning:* {action.reasoning}")
68
 
69
  done = obs.done
70
 
71
  res_eval = "🟒 **EQUITY ACHIEVED**" if obs.fairness_score > 0.8 else "πŸ”΄ **NEGLECT DETECTED**"
72
+ result_text = f"### πŸ† FINAL OUTCOME\n- **Reward Score:** {obs.cumulative_reward:.3f}\n- **Equity Index:** {obs.fairness_score:.3f}\n\n{res_eval}\n\n*Trajectory saved to training_data/ for RL refinement.*"
73
  return "\n".join(logs), result_text
74
 
75
  with gr.Blocks(title="FairRecovery++", theme=gr.themes.Soft()) as gradio_app:
76
+ gr.Markdown("# πŸ—οΈ FairRecovery++: Real-time LLM Simulator")
77
  with gr.Tabs():
78
  with gr.Tab("Simulation"):
79
+ with gr.Row():
80
+ policy = gr.Dropdown(
81
+ choices=["Baseline (Greedy)", "Fairness Aware (Heuristic)", "Live LLM (Llama-3)"],
82
+ value="Baseline (Greedy)",
83
+ label="Agent Strategy"
84
+ )
85
+ hf_token = gr.Textbox(label="Hugging Face Token (optional for Live LLM)", type="password")
86
+
87
+ btn = gr.Button("Run Live Simulation", variant="primary")
88
  logs = gr.Markdown()
89
  results = gr.Markdown()
90
+ btn.click(run_simulation, inputs=[policy, hf_token], outputs=[logs, results])
91
  with gr.Tab("README"):
92
  readme_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "README.md")
93
  with open(readme_path, "r", encoding="utf-8") as f:
94
+ gr.Markdown(f.read())
 
 
 
95
 
96
  @app.get("/")
97
  async def root(): return RedirectResponse(url="/ui/")