Spaces:
Sleeping
Sleeping
joshua400 commited on
Commit Β·
b512de5
1
Parent(s): 39b98d6
π FEAT: Integrated Live HF LLM (Llama-3) & Training Data Logging
Browse files- inference.py +88 -34
- server/app.py +36 -20
inference.py
CHANGED
|
@@ -1,16 +1,94 @@
|
|
| 1 |
"""
|
| 2 |
-
FairRecovery++ β
|
| 3 |
|
| 4 |
-
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
from __future__ import annotations
|
| 8 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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
|
| 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
|
| 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
|
| 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"###
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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++:
|
| 65 |
with gr.Tabs():
|
| 66 |
with gr.Tab("Simulation"):
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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/")
|