准星
Browse files
gradio-web/config.py
CHANGED
|
@@ -24,6 +24,25 @@ SESSION_TIMEOUT = 300 # Session超时时间(秒),如果30秒内没有exec
|
|
| 24 |
# 兜底执行次数配置
|
| 25 |
EXECUTE_LIMIT_OFFSET = 4 # 兜底执行次数 = non_demonstration_task_length + EXECUTE_LIMIT_OFFSET
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
# 应该显示demonstration videos的环境ID列表
|
| 29 |
DEMO_VIDEO_ENV_IDS = [
|
|
|
|
| 24 |
# 兜底执行次数配置
|
| 25 |
EXECUTE_LIMIT_OFFSET = 4 # 兜底执行次数 = non_demonstration_task_length + EXECUTE_LIMIT_OFFSET
|
| 26 |
|
| 27 |
+
TASK_NAME_LIST = [
|
| 28 |
+
"BinFill",
|
| 29 |
+
"StopCube",
|
| 30 |
+
"PickXtimes",
|
| 31 |
+
"SwingXtimes",
|
| 32 |
+
"ButtonUnmask",
|
| 33 |
+
"VideoUnmask",
|
| 34 |
+
"VideoUnmaskSwap",
|
| 35 |
+
"ButtonUnmaskSwap",
|
| 36 |
+
"PickHighlight",
|
| 37 |
+
"VideoRepick",
|
| 38 |
+
"VideoPlaceButton",
|
| 39 |
+
"VideoPlaceOrder",
|
| 40 |
+
"MoveCube",
|
| 41 |
+
"InsertPeg",
|
| 42 |
+
"PatternLock",
|
| 43 |
+
"RouteStick",
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
|
| 47 |
# 应该显示demonstration videos的环境ID列表
|
| 48 |
DEMO_VIDEO_ENV_IDS = [
|
gradio-web/image_utils.py
CHANGED
|
@@ -702,16 +702,17 @@ def draw_coordinate_axes(img, position="right", rotate_180=False, env_id=None):
|
|
| 702 |
|
| 703 |
|
| 704 |
def draw_marker(img, x, y):
|
| 705 |
-
"""Draws a
|
| 706 |
if isinstance(img, np.ndarray):
|
| 707 |
img = Image.fromarray(img)
|
| 708 |
|
| 709 |
img = img.copy()
|
| 710 |
draw = ImageDraw.Draw(img)
|
| 711 |
r = 5
|
|
|
|
| 712 |
# Circle
|
| 713 |
-
draw.ellipse((x-r, y-r, x+r, y+r), outline=
|
| 714 |
# Cross
|
| 715 |
-
draw.line((x-r, y, x+r, y), fill=
|
| 716 |
-
draw.line((x, y-r, x, y+r), fill=
|
| 717 |
return img
|
|
|
|
| 702 |
|
| 703 |
|
| 704 |
def draw_marker(img, x, y):
|
| 705 |
+
"""Draws a fluorescent yellow circle and cross at (x, y)."""
|
| 706 |
if isinstance(img, np.ndarray):
|
| 707 |
img = Image.fromarray(img)
|
| 708 |
|
| 709 |
img = img.copy()
|
| 710 |
draw = ImageDraw.Draw(img)
|
| 711 |
r = 5
|
| 712 |
+
marker_color = (204, 255, 0)
|
| 713 |
# Circle
|
| 714 |
+
draw.ellipse((x-r, y-r, x+r, y+r), outline=marker_color, width=2)
|
| 715 |
# Cross
|
| 716 |
+
draw.line((x-r, y, x+r, y), fill=marker_color, width=2)
|
| 717 |
+
draw.line((x, y-r, x, y+r), fill=marker_color, width=2)
|
| 718 |
return img
|
gradio-web/test/test_reference_action_callbacks.py
CHANGED
|
@@ -25,6 +25,10 @@ class _FakeOptionSession:
|
|
| 25 |
return Image.new("RGB", (24, 24), color=(0, 0, 0))
|
| 26 |
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
def test_on_reference_action_success_updates_option_and_coords(monkeypatch, reload_module):
|
| 29 |
config = reload_module("config")
|
| 30 |
callbacks = reload_module("gradio_callbacks")
|
|
@@ -48,7 +52,7 @@ def test_on_reference_action_success_updates_option_and_coords(monkeypatch, relo
|
|
| 48 |
|
| 49 |
assert img_update.get("__type__") == "update"
|
| 50 |
assert isinstance(img_update.get("value"), Image.Image)
|
| 51 |
-
assert img_update["value"].getpixel((5, 6))
|
| 52 |
assert img_update.get("elem_classes") == config.get_live_obs_elem_classes()
|
| 53 |
assert option_update.get("value") == 2
|
| 54 |
assert coords_text == "5, 6"
|
|
@@ -117,7 +121,7 @@ def test_on_map_click_clears_wait_state_and_restores_action_prompt(monkeypatch,
|
|
| 117 |
|
| 118 |
assert img_update.get("__type__") == "update"
|
| 119 |
assert isinstance(img_update.get("value"), Image.Image)
|
| 120 |
-
assert img_update["value"].getpixel((5, 6))
|
| 121 |
assert img_update.get("elem_classes") == config.get_live_obs_elem_classes()
|
| 122 |
assert coords_text == "5, 6"
|
| 123 |
assert log_text == config.UI_TEXT["log"]["action_selection_prompt"]
|
|
|
|
| 25 |
return Image.new("RGB", (24, 24), color=(0, 0, 0))
|
| 26 |
|
| 27 |
|
| 28 |
+
def _is_fluorescent_yellow(pixel):
|
| 29 |
+
return pixel[0] > 180 and pixel[1] > 200 and pixel[2] < 80
|
| 30 |
+
|
| 31 |
+
|
| 32 |
def test_on_reference_action_success_updates_option_and_coords(monkeypatch, reload_module):
|
| 33 |
config = reload_module("config")
|
| 34 |
callbacks = reload_module("gradio_callbacks")
|
|
|
|
| 52 |
|
| 53 |
assert img_update.get("__type__") == "update"
|
| 54 |
assert isinstance(img_update.get("value"), Image.Image)
|
| 55 |
+
assert _is_fluorescent_yellow(img_update["value"].getpixel((5, 6)))
|
| 56 |
assert img_update.get("elem_classes") == config.get_live_obs_elem_classes()
|
| 57 |
assert option_update.get("value") == 2
|
| 58 |
assert coords_text == "5, 6"
|
|
|
|
| 121 |
|
| 122 |
assert img_update.get("__type__") == "update"
|
| 123 |
assert isinstance(img_update.get("value"), Image.Image)
|
| 124 |
+
assert _is_fluorescent_yellow(img_update["value"].getpixel((5, 6)))
|
| 125 |
assert img_update.get("elem_classes") == config.get_live_obs_elem_classes()
|
| 126 |
assert coords_text == "5, 6"
|
| 127 |
assert log_text == config.UI_TEXT["log"]["action_selection_prompt"]
|
gradio-web/test/test_ui_native_layout_contract.py
CHANGED
|
@@ -97,6 +97,25 @@ def test_render_header_goal_capitalizes_display_value(reload_module):
|
|
| 97 |
assert ui_layout.render_header_goal("") == "—"
|
| 98 |
|
| 99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
def test_native_ui_config_contains_phase_machine_and_precheck_chain(reload_module):
|
| 101 |
ui_layout = reload_module("ui_layout")
|
| 102 |
demo = ui_layout.create_ui_blocks()
|
|
|
|
| 97 |
assert ui_layout.render_header_goal("") == "—"
|
| 98 |
|
| 99 |
|
| 100 |
+
def test_header_task_dropdown_uses_task_name_list_order(reload_module):
|
| 101 |
+
config = reload_module("config")
|
| 102 |
+
ui_layout = reload_module("ui_layout")
|
| 103 |
+
|
| 104 |
+
ui_layout.user_manager.env_choices = list(config.TASK_NAME_LIST)
|
| 105 |
+
demo = ui_layout.create_ui_blocks()
|
| 106 |
+
|
| 107 |
+
try:
|
| 108 |
+
cfg = demo.get_config_file()
|
| 109 |
+
header_task_comp = next(
|
| 110 |
+
comp
|
| 111 |
+
for comp in cfg.get("components", [])
|
| 112 |
+
if comp.get("props", {}).get("elem_id") == "header_task"
|
| 113 |
+
)
|
| 114 |
+
assert header_task_comp.get("props", {}).get("choices") == config.TASK_NAME_LIST
|
| 115 |
+
finally:
|
| 116 |
+
demo.close()
|
| 117 |
+
|
| 118 |
+
|
| 119 |
def test_native_ui_config_contains_phase_machine_and_precheck_chain(reload_module):
|
| 120 |
ui_layout = reload_module("ui_layout")
|
| 121 |
demo = ui_layout.create_ui_blocks()
|
gradio-web/test/test_user_manager_random_flow.py
CHANGED
|
@@ -94,3 +94,22 @@ def test_init_session_fails_when_metadata_root_missing(monkeypatch, reload_modul
|
|
| 94 |
assert success is False
|
| 95 |
assert "No available environments" in msg
|
| 96 |
assert status is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
assert success is False
|
| 95 |
assert "No available environments" in msg
|
| 96 |
assert status is None
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def test_env_choices_follow_task_name_list_order(monkeypatch, reload_module, tmp_path):
|
| 100 |
+
metadata_root = tmp_path / "metadata"
|
| 101 |
+
_write_metadata(metadata_root, "VideoPlaceButton", [0])
|
| 102 |
+
_write_metadata(metadata_root, "BinFill", [0])
|
| 103 |
+
_write_metadata(metadata_root, "PatternLock", [0])
|
| 104 |
+
_write_metadata(metadata_root, "StopCube", [0])
|
| 105 |
+
monkeypatch.setenv("ROBOMME_METADATA_ROOT", str(metadata_root))
|
| 106 |
+
|
| 107 |
+
user_manager_mod = reload_module("user_manager")
|
| 108 |
+
manager = user_manager_mod.UserManager()
|
| 109 |
+
|
| 110 |
+
assert manager.env_choices == [
|
| 111 |
+
"BinFill",
|
| 112 |
+
"StopCube",
|
| 113 |
+
"VideoPlaceButton",
|
| 114 |
+
"PatternLock",
|
| 115 |
+
]
|
gradio-web/user_manager.py
CHANGED
|
@@ -4,6 +4,7 @@ import random
|
|
| 4 |
import threading
|
| 5 |
from pathlib import Path
|
| 6 |
|
|
|
|
| 7 |
from state_manager import clear_task_start_time, get_task_start_time
|
| 8 |
|
| 9 |
|
|
@@ -16,7 +17,7 @@ class UserManager:
|
|
| 16 |
self.lock = threading.Lock()
|
| 17 |
|
| 18 |
self.env_to_episodes = self._load_env_episode_pool()
|
| 19 |
-
self.env_choices =
|
| 20 |
|
| 21 |
# Session-local progress only (no disk persistence)
|
| 22 |
self.session_progress = {}
|
|
@@ -61,6 +62,12 @@ class UserManager:
|
|
| 61 |
print(f"Loaded random env pool: {len(env_to_episodes)} envs from metadata root {metadata_root}")
|
| 62 |
return env_to_episodes
|
| 63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
def _ensure_session_entry(self, uid):
|
| 65 |
if uid not in self.session_progress:
|
| 66 |
self.session_progress[uid] = {
|
|
|
|
| 4 |
import threading
|
| 5 |
from pathlib import Path
|
| 6 |
|
| 7 |
+
from config import TASK_NAME_LIST
|
| 8 |
from state_manager import clear_task_start_time, get_task_start_time
|
| 9 |
|
| 10 |
|
|
|
|
| 17 |
self.lock = threading.Lock()
|
| 18 |
|
| 19 |
self.env_to_episodes = self._load_env_episode_pool()
|
| 20 |
+
self.env_choices = self._build_env_choices()
|
| 21 |
|
| 22 |
# Session-local progress only (no disk persistence)
|
| 23 |
self.session_progress = {}
|
|
|
|
| 62 |
print(f"Loaded random env pool: {len(env_to_episodes)} envs from metadata root {metadata_root}")
|
| 63 |
return env_to_episodes
|
| 64 |
|
| 65 |
+
def _build_env_choices(self):
|
| 66 |
+
available_envs = set(self.env_to_episodes.keys())
|
| 67 |
+
ordered_choices = [env_id for env_id in TASK_NAME_LIST if env_id in available_envs]
|
| 68 |
+
remaining_choices = sorted(available_envs - set(ordered_choices))
|
| 69 |
+
return ordered_choices + remaining_choices
|
| 70 |
+
|
| 71 |
def _ensure_session_entry(self, uid):
|
| 72 |
if uid not in self.session_progress:
|
| 73 |
self.session_progress[uid] = {
|