33
44"""
55import sys
6- from typing import Any
6+ from typing import Any , Annotated
77from loguru import logger
8- from fastapi import APIRouter , Body , status
8+ from fastapi import APIRouter , status , Depends
99from fastapi .responses import JSONResponse
1010from fastapi import HTTPException
11- from api .utils .time_step_response import TimeStep , TimeStepType
11+
12+ from api .utils .make_env_request_model import MakeEnvRequestModel
13+ from api .utils .make_env_response_model import MakeEnvResponseModel
14+ from api .utils .reset_request_model import RestEnvRequestModel
15+ from api .utils .spaces .actions import ContinuousVectorAction
16+ from api .utils .time_step_response import TimeStep , TimeStepType , TimeStepResponse
1217from api .utils .gym_env_manager import GymEnvManager
18+ from api .api_config import get_api_config , Config
1319
1420lunar_lander_continuous_router = APIRouter (prefix = "/gymnasium/lunar-lander-continuous-env" ,
1521 tags = ["Lunar Lander Continuous API" ])
2430 1 : "Side engine throttle — range [-1, 1], controlling left/right orientation." ,
2531 }
2632
33+ DEFAULT_OPTIONS = {'gravity' : - 10.0 , 'enable_wind' : False , 'wind_power' : 15.0 , 'turbulence_power' : 1.5 }
34+ DEFAULT_VERSION = "v3"
35+
2736
2837@lunar_lander_continuous_router .get ("/copies" )
2938async def get_n_copies () -> JSONResponse :
@@ -37,7 +46,7 @@ async def get_action_space() -> JSONResponse:
3746 content = {"action_space" : ACTIONS_SPACE })
3847
3948
40- @lunar_lander_continuous_router .get ("/is-alive" )
49+ @lunar_lander_continuous_router .get ("/{idx}/ is-alive" )
4150async def get_is_alive (idx : str ) -> JSONResponse :
4251 is_alive_ = manager .is_alive (idx = idx )
4352 return JSONResponse (status_code = status .HTTP_200_OK ,
@@ -56,10 +65,15 @@ async def close(idx: str) -> JSONResponse:
5665 content = {"message" : "FAILED" })
5766
5867
59- @lunar_lander_continuous_router .post ("/make" )
60- async def make (version : str = Body (default = "v3" ),
61- options : dict [str , Any ] = Body (default = {'gravity' : - 10.0 , 'enable_wind' : False ,
62- 'wind_power' : 15.0 , 'turbulence_power' : 1.5 })) -> JSONResponse :
68+ @lunar_lander_continuous_router .post ("/make" , status_code = status .HTTP_201_CREATED ,
69+ response_model = MakeEnvResponseModel )
70+ async def make (request : MakeEnvRequestModel ,
71+ api_config : Annotated [Config , Depends (get_api_config )]) -> JSONResponse :
72+ version = request .version or DEFAULT_VERSION
73+
74+ # merge defaults with user overrides
75+ options = DEFAULT_OPTIONS | (request .options or {})
76+
6377 if version == 'v1' or version == 'v2' :
6478 raise HTTPException (status_code = status .HTTP_400_BAD_REQUEST ,
6579 detail = 'Environment version v1 for `LunarLander` '
@@ -70,14 +84,15 @@ async def make(version: str = Body(default="v3"),
7084 options ['continuous' ] = True
7185 idx = await manager .make (env_name = env_type , ** options )
7286
73- logger .info (f'Created environment { ENV_NAME } and index { idx } ' )
87+ if api_config .LOG_INFO :
88+ logger .info (f'Created environment { env_type } with index { idx } ' )
7489 return JSONResponse (status_code = status .HTTP_201_CREATED ,
7590 content = {"message" : "OK" , "idx" : idx })
7691
7792
78- @lunar_lander_continuous_router .post ("/{idx}/reset" )
79- async def reset ( idx : str , seed : int = Body ( default = 42 ),
80- options : dict [ str , Any ] = Body ( default = {}) ) -> JSONResponse :
93+ @lunar_lander_continuous_router .post ("/{idx}/reset" , status_code = status . HTTP_202_ACCEPTED ,
94+ response_model = TimeStepResponse )
95+ async def reset ( idx : str , reset_ops : RestEnvRequestModel ) -> JSONResponse :
8196 """Reset the environment
8297
8398 :return:
@@ -88,7 +103,7 @@ async def reset(idx: str, seed: int = Body(default=42),
88103 detail = {"message" : "NOT_ALIVE/NOT_CREATED" })
89104
90105 try :
91- reset_step = await manager .reset (idx = idx , seed = seed )
106+ reset_step = await manager .reset (idx = idx , seed = reset_ops . seed )
92107
93108 observation = reset_step .observation
94109 observation = [float (val ) for val in observation ]
@@ -107,17 +122,19 @@ async def reset(idx: str, seed: int = Body(default=42),
107122 " Have you called make()?" })
108123
109124
110- @lunar_lander_continuous_router .post ("/{idx}/step" )
111- async def step (idx : str , action : list [float ] = Body (...)) -> JSONResponse :
125+ @lunar_lander_continuous_router .post ("/{idx}/step" , status_code = status .HTTP_202_ACCEPTED ,
126+ response_model = TimeStepResponse )
127+ async def step (idx : str , action : ContinuousVectorAction ,
128+ api_config : Annotated [Config , Depends (get_api_config )]) -> JSONResponse :
112129 if idx not in manager :
113130 raise HTTPException (status_code = status .HTTP_400_BAD_REQUEST ,
114131 detail = {"message" : "NOT_ALIVE/NOT_CREATED. Call make/reset" })
115132
116- if len (action ) != len (ACTIONS_SPACE ):
133+ if len (action . action ) != len (ACTIONS_SPACE ):
117134 raise HTTPException (status_code = status .HTTP_400_BAD_REQUEST ,
118- detail = f"Action vector size { len (action )} != 2" )
135+ detail = f"Action vector size { len (action . action )} != 2" )
119136
120- step_result = await manager .step (idx = idx , action = action )
137+ step_result = await manager .step (idx = idx , action = action . action )
121138
122139 step_type = TimeStepType .MID
123140 if step_result .terminated :
@@ -135,6 +152,7 @@ async def step(idx: str, action: list[float] = Body(...)) -> JSONResponse:
135152 info = step_result .info ,
136153 discount = 1.0 )
137154
138- logger .info (f'Step in environment { ENV_NAME } and index { idx } ' )
155+ if api_config .LOG_INFO :
156+ logger .info (f'Step in environment { ENV_NAME } and index { idx } ' )
139157 return JSONResponse (status_code = status .HTTP_202_ACCEPTED ,
140158 content = {"time_step" : step_ .model_dump ()})
0 commit comments