Anthony Liang commited on
Commit
060091c
·
1 Parent(s): 5ae9357

update with examples

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -32,6 +32,81 @@ from datasets import load_dataset as load_dataset_hf, get_dataset_config_names
32
 
33
  logger = logging.getLogger(__name__)
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  # Predefined dataset names (same as visualizer)
36
  PREDEFINED_DATASETS = [
37
  "abraranwar/agibotworld_alpha_rfm",
@@ -68,10 +143,13 @@ PREDEFINED_DATASETS = [
68
  "jesbu1/usc_koch_p_ranking_rfm",
69
  ]
70
 
 
 
 
71
  # Global server state
72
  _server_state = {
73
- "server_url": None,
74
- "base_url": "https://robometer.a.pinggy.link", # Default: Pinggy tunnel or use http://HOST for port scan
75
  }
76
 
77
 
@@ -536,13 +614,18 @@ def process_two_videos(
536
  predictions = outputs_preference.get("predictions", [])
537
  prediction_probs = outputs_preference.get("prediction_probs", [])
538
 
539
- result_text = f"**Preference Prediction:**\n"
540
  if prediction_probs and len(prediction_probs) > 0:
541
  prob = prediction_probs[0]
542
- result_text += f"- Probability (A preferred): {prob:.3f}\n"
543
- result_text += f"- Interpretation: {'Video A is preferred' if prob > 0.5 else 'Video B is preferred'}\n"
 
 
 
 
 
 
544
  else:
545
- result_text += "Could not extract preference prediction from server response.\n"
546
 
547
  elif prediction_type == "progress":
548
  # Create ProgressSamples for both videos
@@ -605,132 +688,50 @@ with demo:
605
  """
606
  )
607
 
608
- # Hidden state to store server URL and model mapping (define before use)
609
- server_url_state = gr.State(value=None)
610
- model_url_mapping_state = gr.State(value={}) # Maps model_name -> server_url
611
-
612
- # Function definitions for event handlers
613
- def discover_and_select_models(base_url: str):
614
- """Discover models and update dropdown."""
615
- if not base_url:
616
- return (
617
- gr.update(choices=[], value=None),
618
- gr.update(value="Please provide a base URL", visible=True),
619
- gr.update(value="", visible=True),
620
- None,
621
- {}, # Empty mapping
622
- )
623
-
624
- _server_state["base_url"] = base_url
625
- models = discover_available_models(base_url, port_range=(8000, 8010))
626
-
627
- if not models:
628
- return (
629
- gr.update(choices=[], value=None),
630
- gr.update(value="❌ No models found on ports 8000-8010. Make sure servers are running.", visible=True),
631
- gr.update(value="", visible=True),
632
- None,
633
- {}, # Empty mapping
634
- )
635
-
636
- # Format choices: show model_name in dropdown
637
- # Store mapping of model_name to URL in state
638
- choices = []
639
- url_map = {}
640
- for url, name in models:
641
- choices.append(name)
642
- url_map[name] = url
643
-
644
- # Auto-select first model
645
- selected_choice = choices[0] if choices else None
646
- selected_url = url_map.get(selected_choice) if selected_choice else None
647
-
648
- # Get model info for selected model
649
- model_info_text = get_model_info_for_url(selected_url) if selected_url else ""
650
- status_text = f"✅ Found {len(models)} model(s). Auto-selected first model."
651
-
652
- _server_state["server_url"] = selected_url
653
-
654
- return (
655
- gr.update(choices=choices, value=selected_choice),
656
- gr.update(value=status_text, visible=True),
657
- gr.update(value=model_info_text, visible=True),
658
- selected_url,
659
- url_map, # Return mapping for state
660
- )
661
-
662
- def on_model_selected(model_choice: str, url_mapping: dict):
663
- """Handle model selection change."""
664
- if not model_choice:
665
- return (
666
- gr.update(value="No model selected", visible=True),
667
- gr.update(value="", visible=True),
668
- None,
669
- )
670
 
671
- # Get URL from mapping
672
- server_url = url_mapping.get(model_choice) if url_mapping else None
673
-
674
- if not server_url:
675
- return (
676
- gr.update(
677
- value="Could not find server URL for selected model. Please rediscover models.", visible=True
678
- ),
679
- gr.update(value="", visible=True),
680
- None,
681
- )
682
-
683
- # Get model info
684
- model_info_text = get_model_info_for_url(server_url) or ""
685
- status, health_data, _ = check_server_health(server_url)
686
-
687
- _server_state["server_url"] = server_url
688
-
689
- return (
690
- gr.update(value=status, visible=True),
691
- gr.update(value=model_info_text, visible=True),
692
- server_url,
693
- )
694
-
695
- # Use Gradio's built-in Sidebar component (collapsible by default)
696
  with gr.Sidebar():
697
- gr.Markdown("### 🔧 Model Configuration")
698
-
699
- base_url_input = gr.Textbox(
700
- label="Base Server URL",
701
- placeholder="https://robometer.a.pinggy.link or http://40.119.56.66",
702
- value="https://robometer.a.pinggy.link",
703
- interactive=True,
704
- info="Full URL (e.g. Pinggy tunnel) or host; discovery tries URL as-is first, then ports 8000-8010",
705
  )
706
-
707
- discover_btn = gr.Button("🔍 Discover Models", variant="primary", size="lg")
708
-
709
- model_dropdown = gr.Dropdown(
710
- label="Select Model",
711
- choices=[],
712
- value=None,
713
- interactive=True,
714
- info="Click Discover to find the eval server (single URL or ports 8000-8010)",
715
- )
716
-
717
- server_status = gr.Markdown("Click 'Discover Models' to find available models")
718
-
719
  gr.Markdown("---")
720
- gr.Markdown("### 📋 Model Information")
721
- model_info_display = gr.Markdown("")
722
-
723
- # Event handlers for sidebar
724
- discover_btn.click(
725
- fn=discover_and_select_models,
726
- inputs=[base_url_input],
727
- outputs=[model_dropdown, server_status, model_info_display, server_url_state, model_url_mapping_state],
728
- )
729
-
730
- model_dropdown.change(
731
- fn=on_model_selected,
732
- inputs=[model_dropdown, model_url_mapping_state],
733
- outputs=[server_status, model_info_display, server_url_state],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734
  )
735
 
736
  # Main content area with tabs
@@ -748,7 +749,7 @@ with demo:
748
  label="FPS (Frames Per Second)",
749
  minimum=0.1,
750
  maximum=10.0,
751
- value=1.0,
752
  step=0.1,
753
  info="Frames per second to extract from video (higher = more frames)",
754
  )
@@ -788,8 +789,15 @@ with demo:
788
  use_dataset_video_btn = gr.Button("Use Selected Video", variant="secondary")
789
 
790
  with gr.Column():
791
- progress_plot = gr.Image(label="Progress & Success Prediction", height=400)
792
  info_output = gr.Markdown("")
 
 
 
 
 
 
 
793
 
794
  # State variables for dataset
795
  current_dataset_single = gr.State(None)
@@ -1009,7 +1017,7 @@ with demo:
1009
  api_name="process_single_video",
1010
  )
1011
 
1012
- with gr.Tab("Preference Analysis"):
1013
  # Full-width row: two videos side by side
1014
  with gr.Row():
1015
  video_a_input = gr.Video(label="Video A", height=320)
@@ -1022,6 +1030,14 @@ with demo:
1022
  )
1023
  analyze_dual_btn = gr.Button("Compute Preference", variant="primary")
1024
 
 
 
 
 
 
 
 
 
1025
  gr.Markdown("---")
1026
  gr.Markdown("**OR Select from Dataset**")
1027
  gr.Markdown("---")
 
32
 
33
  logger = logging.getLogger(__name__)
34
 
35
+ # Example media: directory for bundled examples
36
+ _EXAMPLES_DIR = Path(__file__).resolve().parent / "examples"
37
+
38
+ def _progress_examples():
39
+ """Progress tab examples from examples/progress/*.mp4 (video path, task)."""
40
+ tasks = [
41
+ "Pick up the red block",
42
+ "Open the drawer",
43
+ "Place the object in the bin",
44
+ "Complete the task",
45
+ ]
46
+ out = []
47
+ progress_dir = _EXAMPLES_DIR / "progress"
48
+ local_videos = sorted(progress_dir.glob("*.mp4")) if progress_dir.exists() else []
49
+ for i, task in enumerate(tasks):
50
+ if i < len(local_videos):
51
+ out.append([str(local_videos[i]), task])
52
+ return out
53
+
54
+ def _preference_examples():
55
+ """Preference examples from examples/preference: blue/green trash bin pair + subopt vs successful pairs."""
56
+ out = []
57
+ pref_dir = _EXAMPLES_DIR / "preference"
58
+ if not pref_dir.exists():
59
+ return []
60
+
61
+ by_stem = {} # stem (no .mp4) -> full path
62
+ for p in pref_dir.glob("*.mp4"):
63
+ stem = p.stem
64
+ by_stem[stem] = str(p)
65
+
66
+ # 1) Explicit pair: open_blue_trash_bin + open_green_trash_bin
67
+ if "open_blue_trash_bin" in by_stem and "open_green_trash_bin" in by_stem:
68
+ out.append([
69
+ by_stem["open_blue_trash_bin"],
70
+ by_stem["open_green_trash_bin"],
71
+ "Open blue trash bin",
72
+ ])
73
+
74
+ # 2) Subopt vs successful: group by base (strip _subopt, _successful, _successful;, _failure)
75
+ def get_base(stem: str) -> Optional[str]:
76
+ if stem.endswith("_successful;"):
77
+ return stem.replace("_successful;", "")
78
+ for suffix in ("_subopt", "_suboptimal", "_successful", "_success", "_failure"):
79
+ if stem.endswith(suffix):
80
+ return stem[: -len(suffix)]
81
+ return None
82
+
83
+ base_to_files = {} # base -> {"subopt": path, "successful": path}
84
+ for stem, path in by_stem.items():
85
+ if stem in ("open_blue_trash_bin", "open_green_trash_bin"):
86
+ continue
87
+ base = get_base(stem)
88
+ if not base:
89
+ continue
90
+ if base not in base_to_files:
91
+ base_to_files[base] = {}
92
+ if "_subopt" in stem:
93
+ base_to_files[base]["subopt"] = path
94
+ elif "_successful" in stem or "_success" in stem:
95
+ base_to_files[base]["successful"] = path
96
+
97
+ pref_tasks = {
98
+ "bread_in_oven": "Put bread in oven",
99
+ "open_red_drawer": "Open the red drawer",
100
+ "put_blue_cup_sink": "Put the blue cup in the sink",
101
+ }
102
+ for base in sorted(base_to_files.keys()):
103
+ d = base_to_files[base]
104
+ if "subopt" in d and "successful" in d:
105
+ task = pref_tasks.get(base, base.replace("_", " ").title())
106
+ out.append([d["subopt"], d["successful"], task])
107
+
108
+ return out
109
+
110
  # Predefined dataset names (same as visualizer)
111
  PREDEFINED_DATASETS = [
112
  "abraranwar/agibotworld_alpha_rfm",
 
143
  "jesbu1/usc_koch_p_ranking_rfm",
144
  ]
145
 
146
+ # Default eval server URL (official Robometer demo)
147
+ DEFAULT_SERVER_URL = "https://robometer.a.pinggy.link"
148
+
149
  # Global server state
150
  _server_state = {
151
+ "server_url": DEFAULT_SERVER_URL,
152
+ "base_url": DEFAULT_SERVER_URL,
153
  }
154
 
155
 
 
614
  predictions = outputs_preference.get("predictions", [])
615
  prediction_probs = outputs_preference.get("prediction_probs", [])
616
 
 
617
  if prediction_probs and len(prediction_probs) > 0:
618
  prob = prediction_probs[0]
619
+ preferred = "A" if prob > 0.5 else "B"
620
+ result_text = (
621
+ 'For task <span style="color: #2563eb; font-weight: 600;">'
622
+ f"{task_text}"
623
+ '</span>, Video <span style="color: #16a34a; font-weight: 700;">'
624
+ f"{preferred}"
625
+ "</span> is more preferred."
626
+ )
627
  else:
628
+ result_text = "Could not extract preference prediction from server response."
629
 
630
  elif prediction_type == "progress":
631
  # Create ProgressSamples for both videos
 
688
  """
689
  )
690
 
691
+ # Hidden state: fixed official server URL (no model selection in UI)
692
+ server_url_state = gr.State(value=DEFAULT_SERVER_URL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
 
694
+ # Sidebar: Robometer branding and official links (no model selection)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
695
  with gr.Sidebar():
696
+ gr.Markdown("## Robometer")
697
+ gr.Markdown(
698
+ "**Official project** · [robometer.github.io](https://robometer.github.io/)"
 
 
 
 
 
699
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  gr.Markdown("---")
701
+ gr.Markdown("### Links")
702
+ gr.HTML(
703
+ """
704
+ <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
705
+ <a href="https://robometer.github.io/" target="_blank" rel="noopener"
706
+ title="Project page"
707
+ style="display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.65rem;
708
+ border-radius: 999px; border: 1px solid #e0e0e0; font-size: 0.9rem;
709
+ text-decoration: none;">
710
+ <span>🌐</span><span>Project</span>
711
+ </a>
712
+ <a href="https://github.com/robometer" target="_blank" rel="noopener"
713
+ title="GitHub"
714
+ style="display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.65rem;
715
+ border-radius: 999px; border: 1px solid #e0e0e0; font-size: 0.9rem;
716
+ text-decoration: none;">
717
+ <span>📂</span><span>Code</span>
718
+ </a>
719
+ <a href="https://huggingface.co/datasets/rewardfm/rbm-1m" target="_blank" rel="noopener"
720
+ title="RBM-1M Dataset"
721
+ style="display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.65rem;
722
+ border-radius: 999px; border: 1px solid #e0e0e0; font-size: 0.9rem;
723
+ text-decoration: none;">
724
+ <span>📊</span><span>Dataset</span>
725
+ </a>
726
+ <a href="https://huggingface.co/aliangdw/Robometer-4B" target="_blank" rel="noopener"
727
+ title="Model weights on Hugging Face"
728
+ style="display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.65rem;
729
+ border-radius: 999px; border: 1px solid #e0e0e0; font-size: 0.9rem;
730
+ text-decoration: none;">
731
+ <span>💾</span><span>Weights</span>
732
+ </a>
733
+ </div>
734
+ """
735
  )
736
 
737
  # Main content area with tabs
 
749
  label="FPS (Frames Per Second)",
750
  minimum=0.1,
751
  maximum=10.0,
752
+ value=3.0,
753
  step=0.1,
754
  info="Frames per second to extract from video (higher = more frames)",
755
  )
 
789
  use_dataset_video_btn = gr.Button("Use Selected Video", variant="secondary")
790
 
791
  with gr.Column():
792
+ progress_plot = gr.Image(label="Progress & Success Prediction", height=320)
793
  info_output = gr.Markdown("")
794
+ gr.Markdown("---")
795
+ gr.Markdown("**Examples**")
796
+ gr.Examples(
797
+ examples=_progress_examples(),
798
+ inputs=[single_video_input, task_text_input],
799
+ label="Click an example to load video and task",
800
+ )
801
 
802
  # State variables for dataset
803
  current_dataset_single = gr.State(None)
 
1017
  api_name="process_single_video",
1018
  )
1019
 
1020
+ with gr.Tab("Preference Prediction"):
1021
  # Full-width row: two videos side by side
1022
  with gr.Row():
1023
  video_a_input = gr.Video(label="Video A", height=320)
 
1030
  )
1031
  analyze_dual_btn = gr.Button("Compute Preference", variant="primary")
1032
 
1033
+ gr.Markdown("---")
1034
+ gr.Markdown("**Examples**")
1035
+ gr.Examples(
1036
+ examples=_preference_examples(),
1037
+ inputs=[video_a_input, video_b_input, task_text_dual],
1038
+ label="Click an example to load Video A, Video B, and task",
1039
+ )
1040
+
1041
  gr.Markdown("---")
1042
  gr.Markdown("**OR Select from Dataset**")
1043
  gr.Markdown("---")
examples/preference/bread_in_oven_subopt.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:812ededdd848bc34de1bd3f1290ccfa0571296111c5950102460e3b012d2170d
3
+ size 60689
examples/preference/bread_in_oven_successful;.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f87ebb21d5466eb589df6ed7584cd37f224b87f01fefdf5a6167fd95c6be05b2
3
+ size 60959
examples/preference/open_blue_trash_bin.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9c85ce7afb40f4cc825707e6ea09ca07fe6d88255173acfda1c1cbdd19493c41
3
+ size 30564
examples/preference/open_green_trash_bin.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f8e6c2a52cd7f96f23733827a160934ed5f94c2bd6fcec9ebb89dd01aa5389d
3
+ size 38631
examples/preference/open_red_drawer_failure.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d80a21a6406626f5ff017cd575ff1ab9ab3b61e5d3bf999d786d1f8c49f4ee58
3
+ size 111948
examples/preference/open_red_drawer_subopt.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:86004b33f041e89e71faff0f368416edb4d573c91172076f3c0b5686786da59f
3
+ size 96750
examples/preference/open_red_drawer_successful.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cd5583193692d6de7c7bb938f6c9edf83b26face351f37e6998760d773ec009a
3
+ size 108547
examples/preference/put_blue_cup_sink_subopt.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bfa6d82902330d547d91e0736ad2482f7e7ae3ef1d5182fd0c51382df8bc90a7
3
+ size 63872
examples/preference/put_blue_cup_sink_successful.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f0db1144cfd2231dd98d958007ff4837674ded785e054655f641f8e6c1494c0f
3
+ size 55591
examples/progress/open_red_drawer_wrist.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cd5583193692d6de7c7bb938f6c9edf83b26face351f37e6998760d773ec009a
3
+ size 108547
examples/progress/push_green_block_in_green_bowl.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b0b98ea35e0618b966a088cc80c70dcc0bc3ee3b013ccf4c891fca1f3fc51086
3
+ size 52731
examples/progress/put_apple_in_tray.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:75bf4fecf558406fe281e8e708a85eedb6b8efb3c999f03bc6eb7e72fc76170e
3
+ size 133180
examples/progress/put_marker_in_cup_fail.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b6ac8f08b9ef21f36aa3fd2f69ced1d3642028d90daba1075510c949d6b890c8
3
+ size 52309