Capitalize
Browse files
gradio-web/config.py
CHANGED
|
@@ -40,9 +40,9 @@ DEMO_VIDEO_ENV_IDS = [
|
|
| 40 |
|
| 41 |
UI_TEXT = {
|
| 42 |
"log": {
|
| 43 |
-
"action_selection_prompt": "
|
| 44 |
-
"keypoint_selection_prompt": "
|
| 45 |
-
"demo_video_prompt": '
|
| 46 |
"session_error": "Session Error",
|
| 47 |
"reference_action_error": "Ground Truth Action Error: {error}",
|
| 48 |
"reference_action_message": "Ground Truth Action: {option_label}. {option_action}",
|
|
@@ -54,11 +54,11 @@ UI_TEXT = {
|
|
| 54 |
},
|
| 55 |
"coords": {
|
| 56 |
"not_needed": "No need for coordinates",
|
| 57 |
-
"select_keypoint": "
|
| 58 |
-
"select_keypoint_before_execute": "
|
| 59 |
},
|
| 60 |
"actions": {
|
| 61 |
-
"keypoint_required_suffix": "🎯",
|
| 62 |
},
|
| 63 |
"errors": {
|
| 64 |
"load_missing_task": "Error loading task: missing current_task",
|
|
|
|
| 40 |
|
| 41 |
UI_TEXT = {
|
| 42 |
"log": {
|
| 43 |
+
"action_selection_prompt": "Please select the action in the left 👈,\nSome actions also need to select keypoint",
|
| 44 |
+
"keypoint_selection_prompt": "Current action needs location input, please click on the image to select key pixel",
|
| 45 |
+
"demo_video_prompt": 'Press "Watch Video Input🎬" to watch a video\nNote: you can only watch the video once',
|
| 46 |
"session_error": "Session Error",
|
| 47 |
"reference_action_error": "Ground Truth Action Error: {error}",
|
| 48 |
"reference_action_message": "Ground Truth Action: {option_label}. {option_action}",
|
|
|
|
| 54 |
},
|
| 55 |
"coords": {
|
| 56 |
"not_needed": "No need for coordinates",
|
| 57 |
+
"select_keypoint": "Please click the keypoint selection image",
|
| 58 |
+
"select_keypoint_before_execute": "Please click the keypoint selection image before execute!",
|
| 59 |
},
|
| 60 |
"actions": {
|
| 61 |
+
"keypoint_required_suffix": " 🎯",
|
| 62 |
},
|
| 63 |
"errors": {
|
| 64 |
"load_missing_task": "Error loading task: missing current_task",
|
gradio-web/test/test_ui_native_layout_contract.py
CHANGED
|
@@ -70,6 +70,14 @@ def test_extract_last_goal_prefers_last_list_item(reload_module):
|
|
| 70 |
assert ui_layout.extract_last_goal("['goal a', 'goal b']") == "goal b"
|
| 71 |
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
def test_native_ui_config_contains_phase_machine_and_precheck_chain(reload_module):
|
| 74 |
ui_layout = reload_module("ui_layout")
|
| 75 |
demo = ui_layout.create_ui_blocks()
|
|
|
|
| 70 |
assert ui_layout.extract_last_goal("['goal a', 'goal b']") == "goal b"
|
| 71 |
|
| 72 |
|
| 73 |
+
def test_render_header_goal_capitalizes_display_value(reload_module):
|
| 74 |
+
ui_layout = reload_module("ui_layout")
|
| 75 |
+
|
| 76 |
+
assert ui_layout.render_header_goal("place cube on target") == "Place cube on target"
|
| 77 |
+
assert ui_layout.render_header_goal("['goal a', 'goal b']") == "Goal b"
|
| 78 |
+
assert ui_layout.render_header_goal("") == "—"
|
| 79 |
+
|
| 80 |
+
|
| 81 |
def test_native_ui_config_contains_phase_machine_and_precheck_chain(reload_module):
|
| 82 |
ui_layout = reload_module("ui_layout")
|
| 83 |
demo = ui_layout.create_ui_blocks()
|
gradio-web/test/test_ui_phase_machine_runtime_e2e.py
CHANGED
|
@@ -87,6 +87,19 @@ def _read_header_task_value(page) -> str | None:
|
|
| 87 |
)
|
| 88 |
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
def _read_coords_box_value(page) -> str | None:
|
| 91 |
return page.evaluate(
|
| 92 |
"""() => {
|
|
@@ -1552,6 +1565,77 @@ def test_header_task_shows_env_after_init(monkeypatch):
|
|
| 1552 |
timeout=5000,
|
| 1553 |
)
|
| 1554 |
assert _read_header_task_value(page) == "PickXtimes"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1555 |
browser.close()
|
| 1556 |
finally:
|
| 1557 |
server.should_exit = True
|
|
|
|
| 87 |
)
|
| 88 |
|
| 89 |
|
| 90 |
+
def _read_header_goal_value(page) -> str | None:
|
| 91 |
+
return page.evaluate(
|
| 92 |
+
"""() => {
|
| 93 |
+
const root = document.getElementById('header_goal');
|
| 94 |
+
if (!root) return null;
|
| 95 |
+
const field = root.querySelector('textarea, input');
|
| 96 |
+
if (!field) return null;
|
| 97 |
+
const value = typeof field.value === 'string' ? field.value.trim() : '';
|
| 98 |
+
return value || null;
|
| 99 |
+
}"""
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
def _read_coords_box_value(page) -> str | None:
|
| 104 |
return page.evaluate(
|
| 105 |
"""() => {
|
|
|
|
| 1565 |
timeout=5000,
|
| 1566 |
)
|
| 1567 |
assert _read_header_task_value(page) == "PickXtimes"
|
| 1568 |
+
assert _read_header_goal_value(page) == "Goal"
|
| 1569 |
+
browser.close()
|
| 1570 |
+
finally:
|
| 1571 |
+
server.should_exit = True
|
| 1572 |
+
thread.join(timeout=10)
|
| 1573 |
+
demo.close()
|
| 1574 |
+
|
| 1575 |
+
|
| 1576 |
+
def test_header_goal_capitalizes_displayed_value_after_init(monkeypatch):
|
| 1577 |
+
ui_layout = importlib.reload(importlib.import_module("ui_layout"))
|
| 1578 |
+
|
| 1579 |
+
fake_obs = np.zeros((24, 24, 3), dtype=np.uint8)
|
| 1580 |
+
fake_obs_img = Image.fromarray(fake_obs)
|
| 1581 |
+
|
| 1582 |
+
def fake_init_app(request=None):
|
| 1583 |
+
_ = request
|
| 1584 |
+
return (
|
| 1585 |
+
"uid-auto",
|
| 1586 |
+
gr.update(visible=True), # main_interface
|
| 1587 |
+
gr.update(value=fake_obs_img, interactive=False), # img_display
|
| 1588 |
+
"ready", # log_output
|
| 1589 |
+
gr.update(choices=[("pick", 0)], value=None), # options_radio
|
| 1590 |
+
"place cube on target", # goal_box
|
| 1591 |
+
"No need for coordinates", # coords_box
|
| 1592 |
+
gr.update(value=None, visible=False), # video_display
|
| 1593 |
+
gr.update(visible=False, interactive=False), # watch_demo_video_btn
|
| 1594 |
+
"PickXtimes (Episode 1)", # task_info_box
|
| 1595 |
+
"Completed: 0", # progress_info_box
|
| 1596 |
+
gr.update(interactive=True), # restart_episode_btn
|
| 1597 |
+
gr.update(interactive=True), # next_task_btn
|
| 1598 |
+
gr.update(interactive=True), # exec_btn
|
| 1599 |
+
gr.update(visible=False), # video_phase_group
|
| 1600 |
+
gr.update(visible=True), # action_phase_group
|
| 1601 |
+
gr.update(visible=True), # control_panel_group
|
| 1602 |
+
gr.update(value="hint"), # task_hint_display
|
| 1603 |
+
gr.update(visible=False), # loading_overlay
|
| 1604 |
+
gr.update(interactive=True), # reference_action_btn
|
| 1605 |
+
)
|
| 1606 |
+
|
| 1607 |
+
monkeypatch.setattr(ui_layout, "init_app", fake_init_app)
|
| 1608 |
+
|
| 1609 |
+
demo = ui_layout.create_ui_blocks()
|
| 1610 |
+
|
| 1611 |
+
port = _free_port()
|
| 1612 |
+
host = "127.0.0.1"
|
| 1613 |
+
root_url = f"http://{host}:{port}/"
|
| 1614 |
+
|
| 1615 |
+
app = FastAPI(title="header-goal-capitalization-test")
|
| 1616 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
| 1617 |
+
|
| 1618 |
+
config = uvicorn.Config(app, host=host, port=port, log_level="error")
|
| 1619 |
+
server = uvicorn.Server(config)
|
| 1620 |
+
thread = threading.Thread(target=server.run, daemon=True)
|
| 1621 |
+
thread.start()
|
| 1622 |
+
_wait_http_ready(root_url)
|
| 1623 |
+
|
| 1624 |
+
try:
|
| 1625 |
+
with sync_playwright() as p:
|
| 1626 |
+
browser = p.chromium.launch(headless=True)
|
| 1627 |
+
page = browser.new_page(viewport={"width": 1280, "height": 900})
|
| 1628 |
+
page.goto(root_url, wait_until="domcontentloaded")
|
| 1629 |
+
page.wait_for_selector("#main_interface_root", state="visible", timeout=15000)
|
| 1630 |
+
page.wait_for_function(
|
| 1631 |
+
"""() => {
|
| 1632 |
+
const root = document.getElementById('header_goal');
|
| 1633 |
+
const input = root ? root.querySelector('textarea, input') : null;
|
| 1634 |
+
return !!input && input.value.trim() === 'Place cube on target';
|
| 1635 |
+
}""",
|
| 1636 |
+
timeout=5000,
|
| 1637 |
+
)
|
| 1638 |
+
assert _read_header_goal_value(page) == "Place cube on target"
|
| 1639 |
browser.close()
|
| 1640 |
finally:
|
| 1641 |
server.should_exit = True
|
gradio-web/ui_layout.py
CHANGED
|
@@ -391,6 +391,23 @@ def extract_last_goal(goal_text):
|
|
| 391 |
return text.split("\n")[0].strip()
|
| 392 |
|
| 393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
def _phase_from_updates(main_interface_update, video_phase_update):
|
| 395 |
if isinstance(main_interface_update, dict) and main_interface_update.get("visible") is False:
|
| 396 |
return PHASE_INIT
|
|
@@ -442,10 +459,6 @@ def create_ui_blocks():
|
|
| 442 |
clean_task = clean_task.split(marker, 1)[0].strip()
|
| 443 |
return " ".join(clean_task.splitlines()).strip() or None
|
| 444 |
|
| 445 |
-
def render_header_goal(goal_text):
|
| 446 |
-
last_goal = extract_last_goal(goal_text or "")
|
| 447 |
-
return last_goal if last_goal else "—"
|
| 448 |
-
|
| 449 |
with gr.Blocks(title="Oracle Planner Interface") as demo:
|
| 450 |
demo.css = CSS
|
| 451 |
|
|
|
|
| 391 |
return text.split("\n")[0].strip()
|
| 392 |
|
| 393 |
|
| 394 |
+
def capitalize_first_letter(text):
|
| 395 |
+
"""Uppercase only the first character for display."""
|
| 396 |
+
if not text:
|
| 397 |
+
return text
|
| 398 |
+
if len(text) == 1:
|
| 399 |
+
return text.upper()
|
| 400 |
+
return text[0].upper() + text[1:]
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def render_header_goal(goal_text):
|
| 404 |
+
"""Render header goal from raw goal text using display-only normalization."""
|
| 405 |
+
last_goal = extract_last_goal(goal_text or "")
|
| 406 |
+
if not last_goal:
|
| 407 |
+
return "—"
|
| 408 |
+
return capitalize_first_letter(last_goal)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
def _phase_from_updates(main_interface_update, video_phase_update):
|
| 412 |
if isinstance(main_interface_update, dict) and main_interface_update.get("visible") is False:
|
| 413 |
return PHASE_INIT
|
|
|
|
| 459 |
clean_task = clean_task.split(marker, 1)[0].strip()
|
| 460 |
return " ".join(clean_task.splitlines()).strip() or None
|
| 461 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
with gr.Blocks(title="Oracle Planner Interface") as demo:
|
| 463 |
demo.css = CSS
|
| 464 |
|