Spaces:
Sleeping
Sleeping
Download app.py from Vaish6/MetaOCT-Simulator: direct link, hf CLI and curl.
- Browser
- Download file 6.1 kB
-
https://huggingface.co/spaces/Vaish6/MetaOCT-Simulator/resolve/main/app.py
- Command line
-
hf download hf://spaces/Vaish6/MetaOCT-Simulator/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Vaish6/MetaOCT-Simulator/resolve/main/app.py
6.1 kB
| """ | |
| MetaOCT: OpenEnv-Compliant REST API Server | |
| Implements POST /reset, POST /step, GET /validate per OpenEnv specification. | |
| Hackathon automated checker compatible. | |
| """ | |
| import json | |
| import os | |
| import asyncio | |
| import threading | |
| from http.server import HTTPServer, BaseHTTPRequestHandler | |
| from dotenv import load_dotenv | |
| from env import MetaOCTEnv, Action | |
| load_dotenv() | |
| # Global environment instance and state | |
| _env = None | |
| _env_lock = threading.Lock() | |
| _loop = None | |
| def get_loop(): | |
| global _loop | |
| if _loop is None or _loop.is_closed(): | |
| _loop = asyncio.new_event_loop() | |
| return _loop | |
| def run_async(coro): | |
| loop = get_loop() | |
| return loop.run_until_complete(coro) | |
| def get_env(difficulty="easy"): | |
| global _env | |
| if _env is None: | |
| _env = MetaOCTEnv(difficulty=difficulty) | |
| return _env | |
| class OpenEnvHandler(BaseHTTPRequestHandler): | |
| def log_message(self, format, *args): pass # Suppress access logs | |
| def send_json(self, code, data): | |
| body = json.dumps(data).encode() | |
| self.send_response(code) | |
| self.send_header("Content-Type", "application/json") | |
| self.send_header("Content-Length", str(len(body))) | |
| self.send_header("Access-Control-Allow-Origin", "*") | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def read_body(self): | |
| length = int(self.headers.get("Content-Length", 0)) | |
| if length: | |
| return json.loads(self.rfile.read(length)) | |
| return {} | |
| def do_OPTIONS(self): | |
| self.send_response(200) | |
| self.send_header("Access-Control-Allow-Origin", "*") | |
| self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") | |
| self.send_header("Access-Control-Allow-Headers", "Content-Type, Authorization") | |
| self.end_headers() | |
| def do_GET(self): | |
| if self.path == "/validate" or self.path == "/openenv/validate" or self.path == "/openenv/info": | |
| task_list = [ | |
| {"id": "easy", "name": "Easy POMDP Diagnosis", "difficulty": "easy", "budget": 1000, "description": "Basic POMDP traversal", "grader": "tasks:grade_easy"}, | |
| {"id": "medium", "name": "Medium Accuracy POMDP", "difficulty": "medium", "budget": 400, "description": "Precision optimization", "grader": "tasks:grade_medium"}, | |
| {"id": "hard", "name": "Hard Zero-Shot POMDP", "difficulty": "hard", "budget": 200, "description": "Extreme resource constraints", "grader": "tasks:grade_hard"} | |
| ] | |
| self.send_json(200, { | |
| "status": "ok", | |
| "name": "MetaOCT", | |
| "version": "1.0.0", | |
| "description": "Multi-step POMDP clinical OCT diagnostic environment", | |
| "tasks": task_list, | |
| "action_space": ["request_oct_scan", "enhance_contrast", "measure_fluid_thickness", "submit_diagnosis"], | |
| "observation_space": ["acquired_scans", "available_budget", "tool_outputs", "step_count"] | |
| }) | |
| elif self.path == "/" or self.path == "/health": | |
| self.send_json(200, {"status": "ok", "message": "MetaOCT API Server is running. Use /validate, /reset, /step endpoints."}) | |
| def do_POST(self): | |
| try: | |
| body = self.read_body() | |
| if self.path in ["/reset", "/openenv/reset"]: | |
| difficulty = body.get("task", body.get("difficulty", "easy")) | |
| with _env_lock: | |
| global _env | |
| _env = MetaOCTEnv(difficulty=difficulty) | |
| obs = run_async(_env.reset()) | |
| self.send_json(200, { | |
| "observation": { | |
| "acquired_scans": obs.acquired_scans, | |
| "available_budget": obs.available_budget, | |
| "tool_outputs": obs.tool_outputs, | |
| "step_count": obs.step_count | |
| }, | |
| "info": {"difficulty": difficulty, "task": difficulty} | |
| }) | |
| elif self.path in ["/step", "/openenv/step"]: | |
| action_data = body.get("action", body) | |
| tool_name = action_data.get("tool_name", action_data.get("name", "submit_diagnosis")) | |
| parameters = action_data.get("parameters", action_data.get("args", {})) | |
| action = Action(tool_name=tool_name, parameters=parameters) | |
| with _env_lock: | |
| env = get_env() | |
| result = run_async(env.step(action)) | |
| obs = result.observation | |
| obs_dict = {} | |
| if obs: | |
| obs_dict = { | |
| "acquired_scans": obs.acquired_scans, | |
| "available_budget": obs.available_budget, | |
| "tool_outputs": obs.tool_outputs, | |
| "step_count": obs.step_count | |
| } | |
| self.send_json(200, { | |
| "observation": obs_dict, | |
| "reward": result.reward, | |
| "done": result.done, | |
| "info": result.info or {} | |
| }) | |
| else: | |
| self.send_json(404, {"error": "Unknown endpoint"}) | |
| except Exception as e: | |
| print(f"[ERROR] {self.path}: {e}", flush=True) | |
| self.send_json(500, {"error": str(e)}) | |
| if __name__ == "__main__": | |
| port = int(os.getenv("GRADIO_SERVER_PORT", "7860")) | |
| # Start HTTP server IMMEDIATELY so checker can connect | |
| server = HTTPServer(("0.0.0.0", port), OpenEnvHandler) | |
| print(f"[INFO] MetaOCT OpenEnv API running on port {port}", flush=True) | |
| print(f"[INFO] Endpoints: POST /reset, POST /step, GET /validate", flush=True) | |
| # Pre-warm environment in background thread | |
| def prewarm(): | |
| print("[INFO] Pre-warming MetaOCT environment...", flush=True) | |
| get_env("easy") | |
| print("[INFO] Environment ready.", flush=True) | |
| t = threading.Thread(target=prewarm, daemon=True) | |
| t.start() | |
| server.serve_forever() | |