interior-env / server /app.py
Interior Env
Fix mode endpoints and OpenEnv multi-mode validator constraints
0085ac8
Raw History Blame Contribute Delete
3.2 kB
import sys
import os
import uuid
from typing import Dict, Any
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
# Add parent directory to sys.path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from env import InteriorEnv
from models import Action
from tasks import TASKS
app = FastAPI(title="Interior Design API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# Static dir is one level up
_static_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static")
if not os.path.exists(_static_dir):
os.makedirs(_static_dir)
app.mount("/static", StaticFiles(directory=_static_dir), name="static")
@app.get("/")
async def serve_dashboard():
index_path = os.path.join(_static_dir, "index.html")
if os.path.exists(index_path):
return FileResponse(index_path)
return {"message": "Welcome to Interior Env API"}
class SessionStartReq(BaseModel):
task_id: str
class SessionStepReq(BaseModel):
session_id: str
action_type: str
item: str
x: int = 0
y: int = 0
rotation: int = 0
_sessions = {}
@app.get("/api/v1/tasks")
async def get_tasks():
return {k: {
"description": v["description"],
"difficulty": v["difficulty"],
"room_size": v["room_size"]
} for k, v in TASKS.items()}
# ── OpenEnv validator required endpoints ──────────────────────────────────────
@app.post("/reset")
async def reset():
"""Required by OpenEnv pre-validation script (validate-submission.sh)."""
return {"status": "ok", "tasks": list(TASKS.keys())}
@app.get("/health")
async def health():
"""Docker HEALTHCHECK support."""
return {"status": "healthy", "tasks": len(TASKS)}
@app.post("/api/v1/session/start")
async def start_session(req: SessionStartReq):
if req.task_id not in TASKS:
raise HTTPException(status_code=400, detail="Invalid task ID")
env = InteriorEnv()
obs = env.reset(TASKS[req.task_id], req.task_id)
sid = str(uuid.uuid4())
_sessions[sid] = {"env": env}
return {"session_id": sid, "observation": obs.model_dump()}
@app.post("/api/v1/session/step")
async def step_session(req: SessionStepReq):
if req.session_id not in _sessions:
raise HTTPException(status_code=400, detail="Invalid session ID")
env = _sessions[req.session_id]["env"]
action = Action(
action_type=req.action_type,
item=req.item,
x=req.x,
y=req.y,
rotation=req.rotation
)
obs, reward, done, info = env.step(action)
return {
"observation": obs.model_dump(),
"reward": reward,
"done": done,
"result": env.get_episode_result().model_dump() if done else None
}
def main():
import uvicorn
uvicorn.run("server.app:app", host="0.0.0.0", port=7860, reload=True)
if __name__ == "__main__":
main()