From c442f04c2b9d55c8046f70e7e30e579883087bc1 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sun, 29 Mar 2026 18:17:50 +0530 Subject: [PATCH 1/4] This commit drop the mesages from UI, once the step is clicked --- src/openenv/core/env_server/gradio_ui.py | 146 ++++++++++++++--------- 1 file changed, 87 insertions(+), 59 deletions(-) diff --git a/src/openenv/core/env_server/gradio_ui.py b/src/openenv/core/env_server/gradio_ui.py index dc1a630bd..12ddd77ea 100644 --- a/src/openenv/core/env_server/gradio_ui.py +++ b/src/openenv/core/env_server/gradio_ui.py @@ -6,9 +6,6 @@ """ Gradio-based web UI for OpenEnv environments. - -Replaces the legacy HTML/JavaScript interface when ENABLE_WEB_INTERFACE is set. -Mount at /web via gr.mount_gradio_app() from create_web_interface_app(). """ from __future__ import annotations @@ -23,17 +20,17 @@ def _escape_md(text: str) -> str: - """Escape Markdown special characters in user-controlled content.""" return re.sub(r"([\\`*_\{\}\[\]()#+\-.!|~>])", r"\\\1", str(text)) def _format_observation(data: Dict[str, Any]) -> str: - """Format reset/step response for Markdown display.""" lines: List[str] = [] obs = data.get("observation", {}) + if isinstance(obs, dict): if obs.get("prompt"): lines.append(f"**Prompt:**\n\n{_escape_md(obs['prompt'])}\n") + messages = obs.get("messages", []) if messages: lines.append("**Messages:**\n") @@ -43,17 +40,19 @@ def _format_observation(data: Dict[str, Any]) -> str: cat = _escape_md(str(msg.get("category", ""))) lines.append(f"- `[{cat}]` Player {sender}: {content}") lines.append("") + reward = data.get("reward") done = data.get("done") + if reward is not None: lines.append(f"**Reward:** `{reward}`") if done is not None: lines.append(f"**Done:** `{done}`") + return "\n".join(lines) if lines else "*No observation data*" def _readme_section(metadata: Optional[EnvironmentMetadata]) -> str: - """README content for the left panel.""" if not metadata or not metadata.readme_content: return "*No README available.*" return metadata.readme_content @@ -63,7 +62,6 @@ def get_gradio_display_title( metadata: Optional[EnvironmentMetadata], fallback: str = "OpenEnv Environment", ) -> str: - """Return the title used for the Gradio app (browser tab and Blocks).""" name = metadata.name if metadata else fallback return f"OpenEnv Agentic Environment: {name}" @@ -76,27 +74,31 @@ def build_gradio_app( title: str = "OpenEnv Environment", quick_start_md: Optional[str] = None, ) -> gr.Blocks: - """ - Build a Gradio Blocks app for the OpenEnv web interface. - - Args: - web_manager: WebInterfaceManager (reset/step_environment, get_state). - action_fields: Field dicts from _extract_action_fields(action_cls). - metadata: Environment metadata for README/name. - is_chat_env: If True, single message textbox; else form from action_fields. - title: App title (overridden by metadata.name when present; see get_gradio_display_title). - quick_start_md: Optional Quick Start markdown (class names already replaced). - - Returns: - gr.Blocks to mount with gr.mount_gradio_app(app, blocks, path="/web"). - """ + readme_content = _readme_section(metadata) display_title = get_gradio_display_title(metadata, fallback=title) + # ----------------------------- + # Core Step Logic + # ----------------------------- + async def step(action_data: Dict[str, Any]): + try: + data = await web_manager.step_environment(action_data) + obs_md = _format_observation(data) + + return ( + obs_md, + json.dumps(data, indent=2), + "Step complete.", + ) + except Exception as e: + return ("", "", f"Error: {e}") + async def reset_env(): try: data = await web_manager.reset_environment() obs_md = _format_observation(data) + return ( obs_md, json.dumps(data, indent=2), @@ -105,27 +107,6 @@ async def reset_env(): except Exception as e: return ("", "", f"Error: {e}") - def _step_with_action(action_data: Dict[str, Any]): - async def _run(): - try: - data = await web_manager.step_environment(action_data) - obs_md = _format_observation(data) - return ( - obs_md, - json.dumps(data, indent=2), - "Step complete.", - ) - except Exception as e: - return ("", "", f"Error: {e}") - - return _run - - async def step_chat(message: str): - if not (message or str(message).strip()): - return ("", "", "Please enter an action message.") - action = {"message": str(message).strip()} - return await _step_with_action(action)() - def get_state_sync(): try: data = web_manager.get_state() @@ -133,34 +114,63 @@ def get_state_sync(): except Exception as e: return f"Error: {e}" + # ----------------------------- + # UI + # ----------------------------- with gr.Blocks(title=display_title) as demo: with gr.Row(): + + # LEFT PANEL with gr.Column(scale=1, elem_classes="col-left"): if quick_start_md: with gr.Accordion("Quick Start", open=True): gr.Markdown(quick_start_md) + with gr.Accordion("README", open=False): gr.Markdown(readme_content) + # RIGHT PANEL with gr.Column(scale=2, elem_classes="col-right"): obs_display = gr.Markdown( - value=("# Playground\n\nClick **Reset** to start a new episode."), + value="# Playground\n\nClick **Reset** to start a new episode." ) + with gr.Group(): + + # ----------------------------- + # CHAT MODE + # ----------------------------- if is_chat_env: action_input = gr.Textbox( label="Action message", placeholder="e.g. Enter your message...", ) + step_inputs = [action_input] + + async def step_chat(message: str): + if not (message and str(message).strip()): + return ("", "", "Please enter an action message.", "") + + action = {"message": str(message).strip()} + obs_md, raw_json, status = await step(action) + + return (obs_md, raw_json, status, "") + step_fn = step_chat + + # ----------------------------- + # FORM MODE + # ----------------------------- else: step_inputs = [] + for field in action_fields: name = field["name"] field_type = field.get("type", "text") label = name.replace("_", " ").title() placeholder = field.get("placeholder", "") + if field_type == "checkbox": inp = gr.Checkbox(label=label) elif field_type == "number": @@ -183,58 +193,76 @@ def get_state_sync(): label=label, placeholder=placeholder, ) + step_inputs.append(inp) - async def step_form(*values): - if not action_fields: - return await _step_with_action({})() + def build_action(values): action_data = {} - for i, field in enumerate(action_fields): - if i >= len(values): - break + for val, field in zip(values, action_fields): name = field["name"] - val = values[i] - if field.get("type") == "checkbox": + field_type = field.get("type") + + if field_type == "checkbox": action_data[name] = bool(val) elif val is not None and val != "": action_data[name] = val - return await _step_with_action(action_data)() + + return action_data + + async def step_form(*values): + action_data = build_action(values) + obs_md, raw_json, status = await step(action_data) + + cleared = [ + False if f.get("type") == "checkbox" else None + for f in action_fields + ] + + return (obs_md, raw_json, status, *cleared) step_fn = step_form + # ----------------------------- + # BUTTONS + # ----------------------------- with gr.Row(): step_btn = gr.Button("Step", variant="primary") reset_btn = gr.Button("Reset", variant="secondary") state_btn = gr.Button("Get state", variant="secondary") + with gr.Row(): - status = gr.Textbox( - label="Status", - interactive=False, - ) + status = gr.Textbox(label="Status", interactive=False) + raw_json = gr.Code( label="Raw JSON response", language="json", interactive=False, ) + # ----------------------------- + # EVENT WIRING + # ----------------------------- reset_btn.click( fn=reset_env, outputs=[obs_display, raw_json, status], ) + step_btn.click( fn=step_fn, inputs=step_inputs, - outputs=[obs_display, raw_json, status], + outputs=[obs_display, raw_json, status, *step_inputs], ) + if is_chat_env: action_input.submit( fn=step_fn, inputs=step_inputs, - outputs=[obs_display, raw_json, status], + outputs=[obs_display, raw_json, status, *step_inputs], ) + state_btn.click( fn=get_state_sync, outputs=[raw_json], ) - return demo + return demo \ No newline at end of file From f04eb32798f4aa4a4c1af98e097d7c6ae6db1d5f Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sun, 29 Mar 2026 20:04:43 +0530 Subject: [PATCH 2/4] Should disappear if reset is clicked as well --- src/openenv/core/env_server/gradio_ui.py | 36 ++++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/openenv/core/env_server/gradio_ui.py b/src/openenv/core/env_server/gradio_ui.py index 12ddd77ea..44d7b8398 100644 --- a/src/openenv/core/env_server/gradio_ui.py +++ b/src/openenv/core/env_server/gradio_ui.py @@ -19,6 +19,9 @@ from .types import EnvironmentMetadata +# ----------------------------- +# Utils +# ----------------------------- def _escape_md(text: str) -> str: return re.sub(r"([\\`*_\{\}\[\]()#+\-.!|~>])", r"\\\1", str(text)) @@ -66,6 +69,9 @@ def get_gradio_display_title( return f"OpenEnv Agentic Environment: {name}" +# ----------------------------- +# Main App Builder +# ----------------------------- def build_gradio_app( web_manager: Any, action_fields: List[Dict[str, Any]], @@ -79,7 +85,18 @@ def build_gradio_app( display_title = get_gradio_display_title(metadata, fallback=title) # ----------------------------- - # Core Step Logic + # Helpers + # ----------------------------- + def clear_inputs(): + if is_chat_env: + return [""] + return [ + False if f.get("type") == "checkbox" else None + for f in action_fields + ] + + # ----------------------------- + # Core Logic # ----------------------------- async def step(action_data: Dict[str, Any]): try: @@ -103,9 +120,10 @@ async def reset_env(): obs_md, json.dumps(data, indent=2), "Environment reset successfully.", + *clear_inputs(), # 🔥 clear inputs ) except Exception as e: - return ("", "", f"Error: {e}") + return ("", "", f"Error: {e}", *clear_inputs()) def get_state_sync(): try: @@ -213,12 +231,12 @@ async def step_form(*values): action_data = build_action(values) obs_md, raw_json, status = await step(action_data) - cleared = [ - False if f.get("type") == "checkbox" else None - for f in action_fields - ] - - return (obs_md, raw_json, status, *cleared) + return ( + obs_md, + raw_json, + status, + *clear_inputs(), + ) step_fn = step_form @@ -244,7 +262,7 @@ async def step_form(*values): # ----------------------------- reset_btn.click( fn=reset_env, - outputs=[obs_display, raw_json, status], + outputs=[obs_display, raw_json, status, *step_inputs], ) step_btn.click( From a581eb1764222f731468c8164930fc00d33617c1 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sun, 29 Mar 2026 20:07:25 +0530 Subject: [PATCH 3/4] Adding new line --- src/openenv/core/env_server/gradio_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openenv/core/env_server/gradio_ui.py b/src/openenv/core/env_server/gradio_ui.py index 44d7b8398..9c1167ed7 100644 --- a/src/openenv/core/env_server/gradio_ui.py +++ b/src/openenv/core/env_server/gradio_ui.py @@ -283,4 +283,4 @@ async def step_form(*values): outputs=[raw_json], ) - return demo \ No newline at end of file + return demo From 8b8aa638b535cd07aed2d677049150950e9874d2 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sun, 29 Mar 2026 20:10:36 +0530 Subject: [PATCH 4/4] Adding docstrings --- src/openenv/core/env_server/gradio_ui.py | 70 ++++++++++++------------ 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/openenv/core/env_server/gradio_ui.py b/src/openenv/core/env_server/gradio_ui.py index 9c1167ed7..7d82b68ed 100644 --- a/src/openenv/core/env_server/gradio_ui.py +++ b/src/openenv/core/env_server/gradio_ui.py @@ -6,6 +6,9 @@ """ Gradio-based web UI for OpenEnv environments. + +Replaces the legacy HTML/JavaScript interface when ENABLE_WEB_INTERFACE is set. +Mount at /web via gr.mount_gradio_app() from create_web_interface_app(). """ from __future__ import annotations @@ -19,14 +22,13 @@ from .types import EnvironmentMetadata -# ----------------------------- -# Utils -# ----------------------------- def _escape_md(text: str) -> str: + """Escape Markdown special characters in user-controlled content.""" return re.sub(r"([\\`*_\{\}\[\]()#+\-.!|~>])", r"\\\1", str(text)) def _format_observation(data: Dict[str, Any]) -> str: + """Format reset/step response for Markdown display.""" lines: List[str] = [] obs = data.get("observation", {}) @@ -56,6 +58,7 @@ def _format_observation(data: Dict[str, Any]) -> str: def _readme_section(metadata: Optional[EnvironmentMetadata]) -> str: + """README content for the left panel.""" if not metadata or not metadata.readme_content: return "*No README available.*" return metadata.readme_content @@ -65,13 +68,11 @@ def get_gradio_display_title( metadata: Optional[EnvironmentMetadata], fallback: str = "OpenEnv Environment", ) -> str: + """Return the title used for the Gradio app (browser tab and Blocks).""" name = metadata.name if metadata else fallback return f"OpenEnv Agentic Environment: {name}" -# ----------------------------- -# Main App Builder -# ----------------------------- def build_gradio_app( web_manager: Any, action_fields: List[Dict[str, Any]], @@ -80,14 +81,26 @@ def build_gradio_app( title: str = "OpenEnv Environment", quick_start_md: Optional[str] = None, ) -> gr.Blocks: + """ + Build a Gradio Blocks app for the OpenEnv web interface. + + Args: + web_manager: WebInterfaceManager (reset/step_environment, get_state). + action_fields: Field dicts from _extract_action_fields(action_cls). + metadata: Environment metadata for README/name. + is_chat_env: If True, single message textbox; else form from action_fields. + title: App title (overridden by metadata.name when present). + quick_start_md: Optional Quick Start markdown. + + Returns: + gr.Blocks to mount with gr.mount_gradio_app(app, blocks, path="/web"). + """ readme_content = _readme_section(metadata) display_title = get_gradio_display_title(metadata, fallback=title) - # ----------------------------- - # Helpers - # ----------------------------- - def clear_inputs(): + def clear_inputs() -> List[Any]: + """Return cleared values for all input components.""" if is_chat_env: return [""] return [ @@ -95,10 +108,8 @@ def clear_inputs(): for f in action_fields ] - # ----------------------------- - # Core Logic - # ----------------------------- async def step(action_data: Dict[str, Any]): + """Execute a single environment step and format the response.""" try: data = await web_manager.step_environment(action_data) obs_md = _format_observation(data) @@ -112,6 +123,7 @@ async def step(action_data: Dict[str, Any]): return ("", "", f"Error: {e}") async def reset_env(): + """Reset the environment and return the initial observation.""" try: data = await web_manager.reset_environment() obs_md = _format_observation(data) @@ -120,25 +132,22 @@ async def reset_env(): obs_md, json.dumps(data, indent=2), "Environment reset successfully.", - *clear_inputs(), # 🔥 clear inputs + *clear_inputs(), ) except Exception as e: return ("", "", f"Error: {e}", *clear_inputs()) def get_state_sync(): + """Fetch the current environment state synchronously.""" try: data = web_manager.get_state() return json.dumps(data, indent=2) except Exception as e: return f"Error: {e}" - # ----------------------------- - # UI - # ----------------------------- with gr.Blocks(title=display_title) as demo: with gr.Row(): - # LEFT PANEL with gr.Column(scale=1, elem_classes="col-left"): if quick_start_md: with gr.Accordion("Quick Start", open=True): @@ -147,7 +156,6 @@ def get_state_sync(): with gr.Accordion("README", open=False): gr.Markdown(readme_content) - # RIGHT PANEL with gr.Column(scale=2, elem_classes="col-right"): obs_display = gr.Markdown( value="# Playground\n\nClick **Reset** to start a new episode." @@ -155,9 +163,6 @@ def get_state_sync(): with gr.Group(): - # ----------------------------- - # CHAT MODE - # ----------------------------- if is_chat_env: action_input = gr.Textbox( label="Action message", @@ -167,6 +172,7 @@ def get_state_sync(): step_inputs = [action_input] async def step_chat(message: str): + """Handle chat-style step input.""" if not (message and str(message).strip()): return ("", "", "Please enter an action message.", "") @@ -177,9 +183,6 @@ async def step_chat(message: str): step_fn = step_chat - # ----------------------------- - # FORM MODE - # ----------------------------- else: step_inputs = [] @@ -214,7 +217,8 @@ async def step_chat(message: str): step_inputs.append(inp) - def build_action(values): + def build_action(values: List[Any]) -> Dict[str, Any]: + """Convert UI input values into action dictionary.""" action_data = {} for val, field in zip(values, action_fields): name = field["name"] @@ -228,6 +232,7 @@ def build_action(values): return action_data async def step_form(*values): + """Handle form-based step input.""" action_data = build_action(values) obs_md, raw_json, status = await step(action_data) @@ -240,16 +245,16 @@ async def step_form(*values): step_fn = step_form - # ----------------------------- - # BUTTONS - # ----------------------------- with gr.Row(): step_btn = gr.Button("Step", variant="primary") reset_btn = gr.Button("Reset", variant="secondary") state_btn = gr.Button("Get state", variant="secondary") with gr.Row(): - status = gr.Textbox(label="Status", interactive=False) + status = gr.Textbox( + label="Status", + interactive=False, + ) raw_json = gr.Code( label="Raw JSON response", @@ -257,9 +262,6 @@ async def step_form(*values): interactive=False, ) - # ----------------------------- - # EVENT WIRING - # ----------------------------- reset_btn.click( fn=reset_env, outputs=[obs_display, raw_json, status, *step_inputs], @@ -283,4 +285,4 @@ async def step_form(*values): outputs=[raw_json], ) - return demo + return demo \ No newline at end of file