""" FastAPI application for the MolOpt environment. """ from typing import Any from fastapi.responses import HTMLResponse, JSONResponse try: from openenv.core.env_server.http_server import create_app from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation from .molopt_environment import DEFAULT_TASK, TASKS, MolOptEnvironment except ImportError: from openenv.core.env_server.http_server import create_app from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation from server.molopt_environment import DEFAULT_TASK, TASKS, MolOptEnvironment def build_custom_task_ui( web_manager: Any, action_fields: Any, metadata: Any, is_chat_env: bool, title: str, quick_start_md: str, ) -> Any: import gradio as gr task_names = list(TASKS.keys()) def task_summary(task_name: str) -> str: task = TASKS[task_name] return ( f"### {task.name}\n" f"- Difficulty: {task.difficulty}\n" f"- Max steps: {task.max_steps}\n" f"- Success threshold: {task.success_threshold}\n" f"- Start SMILES: {task.start_smiles}\n\n" f"{task.description}" ) async def reset_selected_task(task_name: str) -> tuple[dict[str, Any], str]: try: response = await web_manager.reset_environment({"task": task_name}) return response, f"Reset complete for task '{task_name}'." except Exception as exc: return {"error": str(exc)}, f"Reset failed for task '{task_name}'." async def call_get_task_info() -> tuple[dict[str, Any], str]: try: response = await web_manager.step_environment( {"type": "call_tool", "tool_name": "get_task_info", "arguments": {}} ) return response, "Called get_task_info." except Exception as exc: return {"error": str(exc)}, "get_task_info failed." async def call_get_properties() -> tuple[dict[str, Any], str]: try: response = await web_manager.step_environment( {"type": "call_tool", "tool_name": "get_properties", "arguments": {}} ) return response, "Called get_properties." except Exception as exc: return {"error": str(exc)}, "get_properties failed." async def call_modify_molecule(new_smiles: str) -> tuple[dict[str, Any], str]: smiles = new_smiles.strip() if not smiles: return {"error": "SMILES is required."}, "No action sent." try: response = await web_manager.step_environment( { "type": "call_tool", "tool_name": "modify_molecule", "arguments": {"new_smiles": smiles}, } ) return response, f"Called modify_molecule for '{smiles}'." except Exception as exc: return {"error": str(exc)}, "modify_molecule failed." def get_state() -> tuple[dict[str, Any], str]: try: return web_manager.get_state(), "Fetched environment state." except Exception as exc: return {"error": str(exc)}, "get_state failed." with gr.Blocks() as demo: gr.Markdown( "## Task Runner\n" "Use this tab to switch tasks and call tools without manually crafting action JSON." ) task_dropdown = gr.Dropdown( choices=task_names, value=DEFAULT_TASK, label="Select Task", ) task_description = gr.Markdown(task_summary(DEFAULT_TASK)) task_dropdown.change(fn=task_summary, inputs=task_dropdown, outputs=task_description) with gr.Row(): reset_button = gr.Button("Reset With Selected Task", variant="primary") state_button = gr.Button("Get State") smiles_input = gr.Textbox( label="SMILES for modify_molecule", placeholder="e.g. Cc1ccccc1", ) with gr.Row(): task_info_button = gr.Button("Tool: get_task_info") properties_button = gr.Button("Tool: get_properties") modify_button = gr.Button("Tool: modify_molecule") status_text = gr.Textbox(label="Status", interactive=False) response_json = gr.JSON(label="Response") reset_button.click(fn=reset_selected_task, inputs=task_dropdown, outputs=[response_json, status_text]) state_button.click(fn=get_state, outputs=[response_json, status_text]) task_info_button.click(fn=call_get_task_info, outputs=[response_json, status_text]) properties_button.click(fn=call_get_properties, outputs=[response_json, status_text]) modify_button.click(fn=call_modify_molecule, inputs=smiles_input, outputs=[response_json, status_text]) return demo app = create_app( MolOptEnvironment, CallToolAction, CallToolObservation, env_name="molopt_env", gradio_builder=build_custom_task_ui, ) @app.get("/", response_class=HTMLResponse) def root() -> str: return """ MolOpt Environment

MolOpt Environment

This container serves the OpenEnv molecule optimization benchmark.

""".strip() @app.get("/health", response_class=JSONResponse) def health() -> dict: return {"status": "ok", "env": "molopt_env"} def main() -> None: import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) if __name__ == "__main__": main()