Spaces:
Sleeping
Sleeping
Kaushalraj Puwar
refactor: improve physical simulation stability with RK2 integration and update documentation across the environment and task modules
ba6f178 | """FastAPI entry point for the OpenEnv-compliant Thermal Plant Server. | |
| This module provides the HTTP interface for resetting the environment and | |
| executing transition steps. It strictly adheres to the OpenEnv API v1.0 | |
| specification, ensuring compatibility with the cross-model evaluation portal. | |
| """ | |
| import os | |
| from fastapi import FastAPI, HTTPException, Request | |
| from fastapi.responses import JSONResponse | |
| from env.api import ( | |
| ActionRequest, | |
| ResetResponse, | |
| StateResponse, | |
| StepRequest, | |
| StepResponse, | |
| ) | |
| from env.interface import ConcreteOpenEnvInterface | |
| from tasks.registry import normalize_task_id | |
| from utils.constants import DEFAULT_EXTERNAL_EPISODE_ID | |
| # Initialize the FastAPI application | |
| app = FastAPI( | |
| title="Thermal Plant Control Environment", | |
| description="An OpenEnv-compliant environment for a thermal plant control hackathon.", | |
| version="1.0.0", | |
| ) | |
| # Create a singleton instance of our environment interface | |
| env_interface = ConcreteOpenEnvInterface() | |
| async def reset_endpoint(http_request: Request): | |
| """ | |
| Reset the plant to a deterministic initial state. | |
| Accepts an optional task_id and episode_id. This is the entry point for | |
| starting new evaluation episodes. | |
| """ | |
| try: | |
| try: | |
| body = await http_request.json() | |
| except Exception: | |
| body = {} | |
| task_id = normalize_task_id(body.get("task_id", "task1") or "task1") | |
| episode_id = body.get("episode_id", None) | |
| if episode_id is None: | |
| episode_id = DEFAULT_EXTERNAL_EPISODE_ID | |
| else: | |
| episode_id = int(episode_id) | |
| observation = env_interface.reset(task_id=task_id, episode_id=episode_id) | |
| return ResetResponse(observation=observation) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Failed to reset environment: {e}") | |
| def step_endpoint(request: StepRequest): | |
| """ | |
| Executes one time step within the environment using the provided action. | |
| """ | |
| try: | |
| action_dict = request.action.model_dump() | |
| invalid_flag = False | |
| # Clamp inputs and check validity | |
| for k in ("U_target", "F_target"): | |
| val = action_dict[k] | |
| if val < 0.0 or val > 1.0: | |
| invalid_flag = True | |
| action_dict[k] = max(0.0, min(1.0, val)) | |
| observation, reward, done, info = env_interface.step(action_dict) | |
| if invalid_flag: | |
| info["invalid_action"] = True | |
| # Round reward to 2 decimals for judge-facing output | |
| reward = round(float(reward), 2) | |
| raw_state = env_interface.get_state() | |
| return StepResponse( | |
| observation=observation, | |
| raw_state=raw_state, | |
| reward=reward, | |
| done=done, | |
| info=info | |
| ) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Failed to execute step: {e}") | |
| def state_endpoint(): | |
| """ | |
| Retrieve the full-precision, unrounded internal state of the plant. | |
| Intended for debugging, diagnostics, and high-fidelity grading. | |
| """ | |
| try: | |
| state = env_interface.get_state() | |
| return StateResponse(state=state) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Failed to get state: {e}") | |
| def health(): | |
| """Health check endpoint declared in openenv.yaml.""" | |
| return {"status": "healthy"} | |
| def root(): | |
| """Root endpoint to confirm the API is running.""" | |
| return JSONResponse(content={"message": "Thermal Plant Control API is running."}) | |
| def main(): | |
| import uvicorn | |
| port = int(os.getenv("PORT", os.getenv("API_PORT", "7860"))) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |
| if __name__ == "__main__": | |
| main() | |