@@ -90,7 +90,31 @@ def register_routes(self, app: Any) -> None:
9090 if not isinstance (app , FastAPI ):
9191 raise TypeError ("app must be a FastAPI instance" )
9292
93- @app .post ("/reset" , response_model = ResetResponse )
93+ @app .post (
94+ "/reset" ,
95+ response_model = ResetResponse ,
96+ tags = ["Environment Control" ],
97+ summary = "Reset the environment" ,
98+ description = """
99+ Reset the environment to its initial state and return the first observation.
100+
101+ You can optionally provide a seed for reproducibility and an episode_id for tracking.
102+ """ ,
103+ responses = {
104+ 200 : {
105+ "description" : "Environment reset successfully" ,
106+ "content" : {
107+ "application/json" : {
108+ "example" : {
109+ "observation" : {"status" : "ready" , "data" : {}},
110+ "reward" : None ,
111+ "done" : False ,
112+ }
113+ }
114+ },
115+ }
116+ },
117+ )
94118 async def reset (
95119 request : ResetRequest = Body (default_factory = ResetRequest ),
96120 ) -> ResetResponse :
@@ -114,7 +138,56 @@ async def reset(
114138 observation = self .env .reset (** valid_kwargs )
115139 return ResetResponse (** self ._serialize_observation (observation ))
116140
117- @app .post ("/step" , response_model = StepResponse )
141+ @app .post (
142+ "/step" ,
143+ response_model = StepResponse ,
144+ tags = ["Environment Control" ],
145+ summary = "Execute an action in the environment" ,
146+ description = """
147+ Execute an action in the environment and receive the resulting observation.
148+
149+ The action must conform to the environment's action schema, which can be
150+ retrieved from the `/schema/action` endpoint. If the action is invalid,
151+ the endpoint will return HTTP 422 with detailed validation errors.
152+
153+ The response includes:
154+ - **observation**: The environment's response to the action
155+ - **reward**: Optional reward signal (float or None)
156+ - **done**: Boolean indicating if the episode has terminated
157+ """ ,
158+ responses = {
159+ 200 : {
160+ "description" : "Action executed successfully" ,
161+ "content" : {
162+ "application/json" : {
163+ "example" : {
164+ "observation" : {"status" : "success" , "data" : {}},
165+ "reward" : 1.0 ,
166+ "done" : False ,
167+ }
168+ }
169+ },
170+ },
171+ 422 : {
172+ "description" : "Validation error - invalid action format or values" ,
173+ "content" : {
174+ "application/json" : {
175+ "example" : {
176+ "detail" : [
177+ {
178+ "type" : "string_too_short" ,
179+ "loc" : ["body" , "action" , "message" ],
180+ "msg" : "String should have at least 1 character" ,
181+ "input" : "" ,
182+ }
183+ ]
184+ }
185+ }
186+ },
187+ },
188+ 500 : {"description" : "Internal server error during action execution" },
189+ },
190+ )
118191 async def step (request : StepRequest ) -> StepResponse :
119192 """Step endpoint - executes action and returns observation."""
120193 action_data = request .action
@@ -130,7 +203,7 @@ async def step(request: StepRequest) -> StepResponse:
130203
131204 # Handle optional parameters
132205 # Start with all fields from the request, including extra ones, but exclude 'action'
133- kwargs = request .model_dump (exclude_unset = True , exclude = {' action' })
206+ kwargs = request .model_dump (exclude_unset = True , exclude = {" action" })
134207
135208 # Pass arguments only if environment accepts them
136209 sig = inspect .signature (self .env .step )
@@ -150,17 +223,45 @@ async def step(request: StepRequest) -> StepResponse:
150223 # Return serialized observation
151224 return StepResponse (** self ._serialize_observation (observation ))
152225
153- @app .get ("/state" , response_model = State )
226+ @app .get (
227+ "/state" ,
228+ response_model = State ,
229+ tags = ["State Management" ],
230+ summary = "Get current environment state" ,
231+ description = """
232+ Retrieve the current internal state of the environment.
233+
234+ This endpoint allows inspection of the environment state without modifying it.
235+ The structure of the state object is defined by the environment's State model.
236+ """ ,
237+ )
154238 async def get_state () -> State :
155239 """State endpoint - returns current environment state."""
156240 return self .env .state
157241
158- @app .get ("/health" )
242+ @app .get (
243+ "/health" ,
244+ tags = ["Health" ],
245+ summary = "Health check" ,
246+ description = "Check if the environment server is running and healthy." ,
247+ )
159248 async def health () -> Dict [str , str ]:
160249 """Health check endpoint."""
161250 return {"status" : "healthy" }
162251
163- @app .get ("/schema/action" , tags = ["Schema" ])
252+ @app .get (
253+ "/schema/action" ,
254+ tags = ["Schema" ],
255+ summary = "Get action JSON schema" ,
256+ description = """
257+ Get JSON schema for actions accepted by this environment.
258+
259+ Returns the complete JSON schema definition for the Action model,
260+ including all field types, constraints, and validation rules.
261+ This schema can be used to validate actions before sending them
262+ to the environment, or to generate forms in web interfaces.
263+ """ ,
264+ )
164265 async def get_action_schema () -> Dict [str , Any ]:
165266 """
166267 Get JSON schema for actions accepted by this environment.
@@ -175,7 +276,18 @@ async def get_action_schema() -> Dict[str, Any]:
175276 """
176277 return self .action_cls .model_json_schema ()
177278
178- @app .get ("/schema/observation" , tags = ["Schema" ])
279+ @app .get (
280+ "/schema/observation" ,
281+ tags = ["Schema" ],
282+ summary = "Get observation JSON schema" ,
283+ description = """
284+ Get JSON schema for observations returned by this environment.
285+
286+ Returns the complete JSON schema definition for the Observation model,
287+ including all field types and nested structures. This schema describes
288+ what observations the environment will return after actions are executed.
289+ """ ,
290+ )
179291 async def get_observation_schema () -> Dict [str , Any ]:
180292 """
181293 Get JSON schema for observations returned by this environment.
@@ -189,7 +301,18 @@ async def get_observation_schema() -> Dict[str, Any]:
189301 """
190302 return self .observation_cls .model_json_schema ()
191303
192- @app .get ("/schema/state" , tags = ["Schema" ])
304+ @app .get (
305+ "/schema/state" ,
306+ tags = ["Schema" ],
307+ summary = "Get state JSON schema" ,
308+ description = """
309+ Get JSON schema for environment state objects.
310+
311+ Returns the complete JSON schema definition for the State model.
312+ This schema describes the internal state representation of the
313+ environment, which can be queried via the /state endpoint.
314+ """ ,
315+ )
193316 async def get_state_schema () -> Dict [str , Any ]:
194317 """
195318 Get JSON schema for environment state objects.
@@ -305,34 +428,70 @@ def create_fastapi_app(
305428 action_cls : Type [Action ],
306429 observation_cls : Type [Observation ],
307430) -> Any :
308- """
309- Create a FastAPI application with routes for the given environment.
310-
311- Args:
312- env: The Environment instance to serve
313- action_cls: The Action subclass this environment expects
314- observation_cls: The Observation subclass this environment returns
315-
316- Returns:
317- FastAPI application instance with routes registered
318-
319- Example:
320- >>> from envs.coding_env.server import CodeExecutionEnvironment
321- >>> from envs.coding_env.models import CodeAction, CodeObservation
322- >>>
323- >>> env = CodeExecutionEnvironment()
324- >>> app = create_fastapi_app(env, CodeAction, CodeObservation)
325- >>>
326- >>> # Run with: uvicorn module:app --host 0.0.0.0 --port 8000
327- """
431+ """Create a FastAPI application with comprehensive documentation."""
328432 try :
329433 from fastapi import FastAPI
330434 except ImportError :
331435 raise ImportError (
332436 "FastAPI is required. Install with: pip install fastapi uvicorn"
333437 )
334438
335- app = FastAPI (title = "Environment HTTP Server" )
439+ app = FastAPI (
440+ title = "OpenEnv Environment HTTP API" ,
441+ version = "1.0.0" ,
442+ description = """
443+ # OpenEnv Environment HTTP API
444+
445+ HTTP API for interacting with OpenEnv environments through a standardized interface.
446+
447+ ## Features
448+
449+ * **Environment Reset**: Initialize or restart episodes
450+ * **Action Execution**: Send actions and receive observations
451+ * **State Inspection**: Query current environment state
452+ * **Schema Access**: Retrieve JSON schemas for actions and observations
453+
454+ ## Workflow
455+
456+ 1. Call `/reset` to start a new episode and get initial observation
457+ 2. Call `/step` repeatedly with actions to interact with environment
458+ 3. Episode ends when observation returns `done: true`
459+ 4. Call `/state` anytime to inspect current environment state
460+
461+ ## Documentation
462+
463+ * **Swagger UI**: Available at `/docs`
464+ * **ReDoc**: Available at `/redoc`
465+ * **OpenAPI Schema**: Available at `/openapi.json`
466+ """ ,
467+ openapi_tags = [
468+ {
469+ "name" : "Environment Control" ,
470+ "description" : "Core operations for environment interaction (reset, step)" ,
471+ },
472+ {
473+ "name" : "State Management" ,
474+ "description" : "Operations for inspecting environment state" ,
475+ },
476+ {
477+ "name" : "Schema" ,
478+ "description" : "JSON Schema endpoints for actions, observations, and state" ,
479+ },
480+ {"name" : "Health" , "description" : "Service health and status checks" },
481+ ],
482+ docs_url = "/docs" ,
483+ redoc_url = "/redoc" ,
484+ openapi_url = "/openapi.json" ,
485+ contact = {
486+ "name" : "OpenEnv Team" ,
487+ "url" : "https://github.com/meta-pytorch/OpenEnv" ,
488+ },
489+ license_info = {
490+ "name" : "BSD-3-Clause" ,
491+ "url" : "https://github.com/meta-pytorch/OpenEnv/blob/main/LICENSE" ,
492+ },
493+ )
494+
336495 server = HTTPEnvServer (env , action_cls , observation_cls )
337496 server .register_routes (app )
338497 return app
0 commit comments