Skip to content

Commit 8adea3b

Browse files
add step navigation buttons in xray. (#300)
* add step navigation buttons * undo change * fix: keyboard navigation shortcut in xray
1 parent 9b6a33f commit 8adea3b

File tree

1 file changed

+69
-48
lines changed

1 file changed

+69
-48
lines changed

src/agentlab/analyze/agent_xray.py

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,32 @@ def filter_agent_id(self, agent_id: list[tuple]):
164164
}
165165
"""
166166

167+
# Keyboard shortcut JavaScript - based on https://github.com/gradio-app/gradio/issues/6101
168+
shortcut_js = """
169+
<script>
170+
function shortcuts(e) {
171+
var event = document.all ? window.event : e;
172+
switch (e.target.tagName.toLowerCase()) {
173+
case "input":
174+
case "textarea":
175+
case "select":
176+
case "button":
177+
return;
178+
default:
179+
if ((e.key === 'ArrowLeft' || e.key === 'ArrowRight') && (e.metaKey || e.ctrlKey)) {
180+
e.preventDefault();
181+
if (e.key === 'ArrowLeft') {
182+
document.getElementById("prev_btn").click();
183+
} else {
184+
document.getElementById("next_btn").click();
185+
}
186+
}
187+
}
188+
}
189+
document.addEventListener('keydown', shortcuts, false);
190+
</script>
191+
"""
192+
167193

168194
def run_gradio(results_dir: Path):
169195
"""
@@ -173,14 +199,12 @@ def run_gradio(results_dir: Path):
173199
global info
174200
info.results_dir = results_dir
175201

176-
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
202+
with gr.Blocks(theme=gr.themes.Soft(), css=css, head=shortcut_js) as demo:
177203
agent_id = gr.State(value=None)
178204
episode_id = gr.State(value=EpisodeId())
179205
agent_task_id = gr.State(value=None)
180206
step_id = gr.State(value=None)
181207

182-
hidden_key_input = gr.Textbox(visible=False, elem_id="key_capture")
183-
184208
with gr.Accordion("Help", open=False):
185209
gr.Markdown(
186210
"""\
@@ -302,6 +326,16 @@ def run_gradio(results_dir: Path):
302326
action_info = gr.Markdown(label="Action Info", elem_classes="my-markdown")
303327
state_error = gr.Markdown(label="Next Step Error", elem_classes="my-markdown")
304328

329+
with gr.Row(variant="panel", elem_classes=["items-center", "justify-center"]):
330+
step_indicator = gr.Markdown("### Step 0/0", elem_classes=["text-center"])
331+
prev_btn = gr.Button(
332+
"◀ Previous", size="md", scale=0, elem_id="prev_btn", elem_classes=["mx-auto"]
333+
)
334+
next_btn = gr.Button(
335+
"Next ▶", size="md", scale=0, elem_id="next_btn", elem_classes=["mx-auto"]
336+
)
337+
gr.Markdown("(Shortcut: Ctrl/Cmd + ← →)", elem_classes=["text-center"])
338+
305339
profiling_gr = gr.Image(
306340
label="Profiling", show_label=False, interactive=False, show_download_button=False
307341
)
@@ -511,31 +545,37 @@ def run_gradio(results_dir: Path):
511545

512546
demo.load(fn=refresh_exp_dir_choices, inputs=exp_dir_choice, outputs=exp_dir_choice)
513547

514-
demo.load(
515-
None,
516-
None,
517-
None,
518-
js="""
519-
function() {
520-
document.addEventListener('keydown', function(e) {
521-
if ((e.key === 'ArrowLeft' || e.key === 'ArrowRight') && (e.metaKey || e.ctrlKey)) {
522-
e.preventDefault();
523-
const hiddenInput = document.querySelector('#key_capture input, #key_capture textarea');
524-
if (hiddenInput) {
525-
let event = e.key === 'ArrowLeft' ? 'Cmd+Left' : 'Cmd+Right';
526-
hiddenInput.value = event;
527-
hiddenInput.dispatchEvent(new Event('input', {bubbles: true}));
528-
}
529-
}
530-
});
531-
}
532-
""",
533-
)
534-
hidden_key_input.change(
535-
handle_key_event,
536-
inputs=[hidden_key_input, step_id],
537-
outputs=[hidden_key_input, step_id],
538-
)
548+
# Simple navigation button events
549+
def navigate_prev(step_id: StepId):
550+
global info
551+
if step_id and step_id.step is not None and step_id.episode_id:
552+
step = max(0, step_id.step - 1)
553+
info.step = step
554+
return StepId(episode_id=step_id.episode_id, step=step)
555+
return step_id
556+
557+
def navigate_next(step_id: StepId):
558+
global info
559+
if step_id and step_id.step is not None and step_id.episode_id and info.exp_result:
560+
step = min(len(info.exp_result.steps_info) - 1, step_id.step + 1)
561+
info.step = step
562+
return StepId(episode_id=step_id.episode_id, step=step)
563+
return step_id
564+
565+
prev_btn.click(navigate_prev, inputs=[step_id], outputs=[step_id])
566+
next_btn.click(navigate_next, inputs=[step_id], outputs=[step_id])
567+
568+
# Update step indicator display
569+
def format_step_indicator(step_id):
570+
global info
571+
if not step_id or not info.exp_result or not info.exp_result.steps_info:
572+
return "### Step 0/0"
573+
# 1-based for user, total steps is len-1 (last is terminal)
574+
current = (step_id.step + 1) if step_id.step is not None else 0
575+
total = max(len(info.exp_result.steps_info) - 1, 0)
576+
return f"### Step {current}/{total}"
577+
578+
step_id.change(format_step_indicator, inputs=[step_id], outputs=[step_indicator])
539579

540580
demo.queue()
541581

@@ -546,25 +586,6 @@ def run_gradio(results_dir: Path):
546586
demo.launch(server_port=port, share=do_share)
547587

548588

549-
def handle_key_event(key_event, step_id: StepId):
550-
551-
if key_event:
552-
global info
553-
554-
# print(f"Key event: {key_event}")
555-
step = step_id.step
556-
if key_event.startswith("Cmd+Left"):
557-
step = max(0, step - 1)
558-
elif key_event.startswith("Cmd+Right"):
559-
step = min(len(info.exp_result.steps_info) - 2, step + 1)
560-
else:
561-
return gr.update()
562-
# print(f"Updating step to {step} from key event {key_event}")
563-
info.step = step
564-
step_id = StepId(episode_id=step_id.episode_id, step=step)
565-
return ("", step_id)
566-
567-
568589
def tab_select(evt: gr.SelectData):
569590
global info
570591
info.active_tab = evt.value
@@ -947,7 +968,7 @@ def get_episode_info(info: Info):
947968

948969
info = f"""\
949970
### {env_args.task_name} (seed: {env_args.task_seed})
950-
### Step {info.step} / {len(steps_info) - 1} (Reward: {cum_reward:.1f})
971+
### (Reward: {cum_reward:.1f})
951972
952973
**Goal:**
953974

0 commit comments

Comments
 (0)