Spaces:
Sleeping
Sleeping
| """ | |
| server/app.py – FastAPI server for Smart Farm Resource Manager. | |
| Exposes: | |
| POST /reset -> Observation | |
| POST /step -> StepResult | |
| GET /state -> dict | |
| GET /health -> dict | |
| GET /tasks -> dict | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| from typing import Any, Dict | |
| import uvicorn | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from src.envs.smart_farm_env.models import Action, Observation, StepResult, TaskConfig | |
| from src.envs.smart_farm_env.server.environment import SmartFarmEnv | |
| # --------------------------------------------------------------------------- | |
| # App | |
| # --------------------------------------------------------------------------- | |
| app = FastAPI( | |
| title="Smart Farm Resource Manager", | |
| description="OpenEnv-compatible precision agriculture simulation API", | |
| version="1.0.0", | |
| docs_url="/docs", # default, but explicit | |
| redoc_url="/redoc", | |
| ) | |
| async def root(): | |
| return { | |
| "message": "🚜 Smart Farm Resource Manager API is running!", | |
| "version": "1.0.0", | |
| "docs": "/docs", | |
| "redoc": "/redoc", | |
| "available_endpoints": [ | |
| "/health", | |
| "/reset", | |
| "/step", | |
| "/state", | |
| "/tasks" | |
| ] | |
| } | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Singleton env (one active episode per server instance) | |
| _env: SmartFarmEnv | None = None | |
| # Load task configs once at startup | |
| _TASKS_PATH = os.path.join(os.path.dirname(__file__), "..", "tasks", "tasks.json") | |
| with open(_TASKS_PATH) as _f: | |
| _tasks_raw = json.load(_f)["tasks"] | |
| TASK_MAP: Dict[str, TaskConfig] = {t["name"]: TaskConfig(**t) for t in _tasks_raw} | |
| def _require_env() -> SmartFarmEnv: | |
| if _env is None: | |
| raise HTTPException(status_code=400, detail="Not initialised — call POST /reset first.") | |
| return _env | |
| # --------------------------------------------------------------------------- | |
| # Endpoints | |
| # --------------------------------------------------------------------------- | |
| def health() -> Dict[str, str]: | |
| return {"status": "ok", "env": "smart-farm-env"} | |
| def reset(task_name: str = "easy") -> Observation: | |
| global _env | |
| if task_name not in TASK_MAP: | |
| raise HTTPException( | |
| status_code=404, | |
| detail=f"Unknown task '{task_name}'. Available: {list(TASK_MAP)}", | |
| ) | |
| _env = SmartFarmEnv(TASK_MAP[task_name]) | |
| return _env.reset() | |
| def step(action: Action) -> StepResult: | |
| env = _require_env() | |
| try: | |
| return env.step(action) | |
| except RuntimeError as exc: | |
| raise HTTPException(status_code=400, detail=str(exc)) | |
| def state() -> Dict[str, Any]: | |
| return _require_env().state() | |
| def list_tasks() -> Dict[str, Any]: | |
| return { | |
| "tasks": [ | |
| { | |
| "name": t.name, | |
| "description": t.description, | |
| "num_plots": t.num_plots, | |
| "allowed_actions": t.allowed_actions, | |
| } | |
| for t in TASK_MAP.values() | |
| ] | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Entry point (used by server Dockerfile CMD) | |
| # --------------------------------------------------------------------------- | |
| if __name__ == "__main__": | |
| port = int(os.environ.get("PORT", 7860)) | |
| uvicorn.run("src.envs.smart_farm_env.server.app:app", host="0.0.0.0", port=port, reload=False) | |
| def main(): | |
| import uvicorn | |
| uvicorn.run(app, host='0.0.0.0', port=7860) | |
| if __name__ == '__main__': | |
| main() | |