NetZero-Nav / netzero_nav /server.py
Aryanshh
feat: Full OpenEnv spec compliance — /metadata, /schema, /health, /mcp endpoints + pyproject.toml fix + deterministic 0-1 graders + proper inference.py STDOUT
a95dc70
Raw
History Blame Contribute Delete
3.64 kB
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import Optional
import os
from netzero_nav.env import NetZeroEnv
from netzero_nav.models import Action, Observation, StepResponse
app = FastAPI(title="NetZero Nav: Eco-Resilient Logistics", version="1.0.0")
_env = NetZeroEnv()
# Mount Static Dashboard
static_path = os.path.join(os.getcwd(), "dashboard")
app.mount("/dashboard", StaticFiles(directory=static_path), name="dashboard")
@app.get("/")
def root():
return RedirectResponse(url="/dashboard/index.html")
class ResetRequest(BaseModel):
task: Optional[str] = "easy"
seed: Optional[int] = 42
# --- OpenEnv Required Endpoints ---
@app.get("/health")
def health():
return {"status": "healthy"}
@app.get("/metadata")
def metadata():
return {
"name": "netzero-nav",
"description": "Autonomous RL logistics agent navigating global disruptions with a Net-Zero carbon mandate.",
"version": "1.0.0",
"tasks": ["easy", "medium", "hard"],
"tags": ["logistics", "esg", "supply-chain", "rl"]
}
@app.get("/schema")
def schema():
return {
"action": {
"action_type": {"type": "string", "enum": ["order_parts", "produce", "offset", "skip", "cancel"]},
"part_type": {"type": "string", "optional": True},
"quantity": {"type": "integer", "optional": True},
"mode": {"type": "string", "enum": ["sea", "air", "rail", "road"], "optional": True},
"product": {"type": "string", "optional": True},
"offset_amount": {"type": "number", "optional": True},
"shipment_id": {"type": "string", "optional": True}
},
"observation": {
"step": {"type": "integer"},
"inventory": {"type": "object"},
"active_shipments": {"type": "array"},
"pending_orders": {"type": "array"},
"carbon_total": {"type": "number"},
"carbon_quota": {"type": "number"},
"cash_balance": {"type": "number"},
"news": {"type": "string", "optional": True}
},
"state": {
"step": {"type": "integer"},
"inventory": {"type": "object"},
"carbon_total": {"type": "number"},
"cash_balance": {"type": "number"},
"orders": {"type": "array"},
"active_shipments": {"type": "array"}
}
}
@app.post("/mcp")
def mcp(body: dict = None):
return {
"jsonrpc": "2.0",
"id": (body or {}).get("id"),
"result": {
"tools": ["reset", "step", "state", "metadata", "schema"],
"env": "netzero-nav"
}
}
# --- Core Env Endpoints ---
@app.post("/reset", response_model=Observation)
def reset(req: ResetRequest = None):
if req is None:
req = ResetRequest()
_env.task = req.task or "easy"
return _env.reset(seed=req.seed)
@app.post("/step", response_model=StepResponse)
def step(action: Action):
obs, reward, done, info = _env.step(action)
return StepResponse(observation=obs, reward=reward, done=done, info=info)
@app.post("/trigger")
def trigger():
_env.sea_blocked_until = _env.step_count + 7
return {"status": "Suez Jam Triggered", "duration": 7}
@app.get("/state")
def state():
return {
"step": _env.step_count,
"inventory": _env.inventory,
"carbon": _env.carbon_total,
"cash": _env.cash_balance,
"orders": _env.pending_orders,
"active_shipments": _env.active_shipments
}