Spaces:
Sleeping
Sleeping
File size: 6,518 Bytes
a871dae 6c3ac09 a871dae | 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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | """
FastAPI application for the Autonomous Traffic Control OpenEnv environment.
Uses the openenv-core create_app() factory which automatically provides:
- POST /reset β start a new episode
- POST /step β execute one action
- GET /state β retrieve episode-level state
- GET /schema β action / observation JSON schemas
- WS /ws β WebSocket endpoint for persistent sessions
- GET /health β liveness probe
- GET /docs β interactive Swagger UI
Adds custom:
- POST /grade β run the automated task grader (0-1 score)
Usage:
uvicorn traffic_control_env.server.app:app --host 0.0.0.0 --port 8000 --reload
python -m traffic_control_env.server.app
"""
import sys
import os
# Ensure project root is on sys.path so `models`, `client`, etc. resolve correctly
# regardless of which directory uvicorn is launched from.
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_SERVER = os.path.dirname(os.path.abspath(__file__))
for _p in (_ROOT, _SERVER):
if _p not in sys.path:
sys.path.insert(0, _p)
from openenv.core.env_server.http_server import create_app
from fastapi import Request, HTTPException
from models import TrafficAction, TrafficObservation
from traffic_control import TrafficControlEnvironment
from tasks import grade as run_grader
# ---------------------------------------------------------------------------
# 1. Build the standard OpenEnv app via create_app()
# ---------------------------------------------------------------------------
app = create_app(
TrafficControlEnvironment,
TrafficAction,
TrafficObservation,
env_name="traffic_control_env",
max_concurrent_envs=4,
)
# ---------------------------------------------------------------------------
# 2. Add the /grade endpoint (not part of openenv-core standard, but needed
# for hackathon evaluation of task scores)
# ---------------------------------------------------------------------------
@app.post("/grade", tags=["eval"])
async def grade(request: Request):
"""
Run the automated grader for the environment's *current* state and return
a 0-1 normalised score along with detailed metrics and feedback.
The request body may optionally contain {"task_id": "..."} to override
auto-detection; otherwise the grader reads task_id from the env state.
Note: For WebSocket sessions the client calls this via HTTP after the
episode ends. The endpoint reads state from the single shared environment
(or first available) β for production, pass task_id + metrics explicitly.
"""
body = {}
try:
body = await request.json()
except Exception:
pass
# Pull metrics either from body (explicit) or from environment manager
task_id = body.get("task_id", "basic_flow")
total_vehicles = int(body.get("total_vehicles_passed", 0))
total_emergency = int(body.get("total_emergency_passed", 0))
total_waiting = float(body.get("total_waiting_time", 0.0))
total_collisions = int(body.get("total_collisions", 0))
total_emergency_delay = float(body.get("total_emergency_delay", 0.0))
total_phase_changes = int(body.get("total_phase_changes", 0))
step_count = int(body.get("step_count", 1))
result = run_grader(
task_id,
total_vehicles_passed=total_vehicles,
total_emergency_passed=total_emergency,
total_waiting_time=total_waiting,
total_collisions=total_collisions,
total_emergency_delay=total_emergency_delay,
total_phase_changes=total_phase_changes,
step_count=step_count,
)
return {
"task_id": task_id,
"score": result.score,
"metrics": result.metrics,
"feedback": result.feedback,
}
# ---------------------------------------------------------------------------
# 3. Add Gradio UI Testing Interface mounted at /ui
# ---------------------------------------------------------------------------
import gradio as gr
import requests
def reset_env(task_id):
try:
resp = requests.post("http://127.0.0.1:8000/reset", json={"task_id": task_id, "seed": 42})
return resp.json() if resp.status_code == 200 else {"error": resp.text}
except Exception as e:
return {"error": str(e)}
def step_env(phase):
try:
resp = requests.post("http://127.0.0.1:8000/step", json={"light_phase": int(phase)})
return resp.json() if resp.status_code == 200 else {"error": resp.text}
except Exception as e:
return {"error": str(e)}
def get_state():
try:
resp = requests.get("http://127.0.0.1:8000/state")
return resp.json() if resp.status_code == 200 else {"error": resp.text}
except Exception as e:
return {"error": str(e)}
with gr.Blocks(title="Traffic Control UI", theme=gr.themes.Soft()) as ui:
gr.Markdown("# π¦ Autonomous Traffic Control - Testing UI")
gr.Markdown("Use this interface to manually test the OpenEnv HTTP API endpoints.")
with gr.Row():
task_dropdown = gr.Dropdown(choices=["basic_flow", "emergency_priority", "dynamic_scenarios"], value="basic_flow", label="Task ID")
reset_btn = gr.Button("π Reset Environment")
state_btn = gr.Button("π Get State")
with gr.Row():
phase_radio = gr.Radio(choices=[("0 - NS Green", "0"), ("1 - EW Green", "1"), ("2 - All Red", "2")], value="0", label="Next Action (Light Phase)")
step_btn = gr.Button("βΆοΈ Step Action", variant="primary")
output_json = gr.JSON(label="API Response")
reset_btn.click(reset_env, inputs=[task_dropdown], outputs=[output_json])
step_btn.click(step_env, inputs=[phase_radio], outputs=[output_json])
state_btn.click(get_state, outputs=[output_json])
app = gr.mount_gradio_app(app, ui, path="/ui")
def main(host: str = "0.0.0.0", port: int = 8000) -> None:
"""
Entry point for direct execution.
uv run --project . server
python -m traffic_control_env.server.app
uvicorn traffic_control_env.server.app:app --reload
"""
import uvicorn
uvicorn.run(app, host=host, port=port)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=8000)
args = parser.parse_args()
# Call main() so openenv validate passes its string check
main(host="0.0.0.0", port=args.port)
|