1212import websockets
1313
1414WS_URL = "ws://127.0.0.1:8000/ws/test"
15- AGENT_DIR = Path (__file__ ).parent .parent
16- TESTS_DIR = AGENT_DIR / "tests"
15+ DEFAULT_AGENT_DIR = Path (__file__ ).parent .parent
16+ DEFAULT_TESTS_DIR = DEFAULT_AGENT_DIR / "tests"
1717
1818ws_conn = None
1919message_queue = Queue ()
2323uvicorn_process = None
2424
2525
26- def scan_agent_scripts ():
27- exclude = {"app.py" , "gradio_chat.py" , "__init__.py" }
28- return [f .name for f in sorted (AGENT_DIR .glob ("*.py" )) if f .name not in exclude ]
26+ def scan_agent_scripts (agent_dir_path ):
27+ try :
28+ if not agent_dir_path :
29+ return []
30+ agent_dir = Path (agent_dir_path )
31+ if not agent_dir .exists ():
32+ print (f"Warning: Agent directory does not exist: { agent_dir_path } " )
33+ return []
34+ if not agent_dir .is_dir ():
35+ print (f"Warning: Agent path is not a directory: { agent_dir_path } " )
36+ return []
37+ exclude = {"app.py" , "gradio_chat.py" , "__init__.py" }
38+ scripts = [f .name for f in sorted (agent_dir .glob ("*.py" )) if f .name not in exclude ]
39+ return scripts
40+ except Exception as e :
41+ print (f"Error scanning agent scripts: { e } " )
42+ return []
2943
3044
31- def scan_script_dirs ():
32- if not TESTS_DIR .exists ():
45+ def scan_script_dirs (scripts_dir_path ):
46+ try :
47+ if not scripts_dir_path :
48+ return []
49+ scripts_dir = Path (scripts_dir_path )
50+ if not scripts_dir .exists ():
51+ print (f"Warning: Scripts directory does not exist: { scripts_dir_path } " )
52+ return []
53+ if not scripts_dir .is_dir ():
54+ print (f"Warning: Scripts path is not a directory: { scripts_dir_path } " )
55+ return []
56+ dirs = [d .name for d in sorted (scripts_dir .iterdir ()) if d .is_dir () and not d .name .startswith ("_" )]
57+ return dirs
58+ except Exception as e :
59+ print (f"Error scanning script directories: { e } " )
3360 return []
34- return [d .name for d in sorted (TESTS_DIR .iterdir ()) if d .is_dir () and not d .name .startswith ("_" )]
3561
3662
3763
@@ -71,23 +97,37 @@ async def _run():
7197 loop .close ()
7298
7399
100+ _init_agents = scan_agent_scripts (str (DEFAULT_AGENT_DIR ))
101+ _init_tests = scan_script_dirs (str (DEFAULT_TESTS_DIR ))
102+
74103with gr .Blocks () as demo :
75- gr .Markdown ("### libEnsemble Agent" )
104+ with gr .Row ():
105+ gr .Markdown ("### libEnsemble Agent" )
106+ with gr .Column (scale = 0 , min_width = 60 ):
107+ settings_btn = gr .Button ("⚙️" , size = "sm" )
108+
109+ agent_dir_state = gr .State (value = str (DEFAULT_AGENT_DIR ))
110+ scripts_dir_state = gr .State (value = str (DEFAULT_TESTS_DIR ))
111+ settings_visible = gr .State (value = False )
112+
113+ with gr .Column (visible = False ) as settings_modal :
114+ with gr .Column (elem_classes = "modal-content" ):
115+ gr .Markdown ("### Settings" )
116+ agent_dir_input = gr .Textbox (label = "Agent Directory" , value = str (DEFAULT_AGENT_DIR ))
117+ scripts_dir_input = gr .Textbox (label = "Scripts Directory" , value = str (DEFAULT_TESTS_DIR ))
118+ with gr .Row ():
119+ apply_settings_btn = gr .Button ("Apply" , variant = "primary" , scale = 1 )
120+ close_settings_btn = gr .Button ("Close" , scale = 1 )
76121
77122 with gr .Row ():
78123 agent_dropdown = gr .Dropdown (
79- label = "Agent Script" ,
80- choices = scan_agent_scripts (),
81- value = scan_agent_scripts ()[0 ] if scan_agent_scripts () else None ,
82- allow_custom_value = True ,
83- scale = 2
124+ label = "Agent Script" , choices = _init_agents ,
125+ value = _init_agents [0 ] if _init_agents else None ,
126+ allow_custom_value = True , scale = 2
84127 )
85128 scripts_dropdown = gr .Dropdown (
86- label = "Scripts Directory" ,
87- choices = scan_script_dirs (),
88- value = None ,
89- allow_custom_value = True ,
90- scale = 2
129+ label = "Scripts Directory" , choices = _init_tests ,
130+ value = None , allow_custom_value = True , scale = 2
91131 )
92132 run_btn = gr .Button ("Run" , variant = "primary" , scale = 1 )
93133 reset_btn = gr .Button ("Reset" , variant = "stop" , scale = 1 )
@@ -166,12 +206,34 @@ def process_messages(scripts_state):
166206 yield logs , scripts , gr .update (choices = script_names , value = selected_script ), scripts .get (selected_script , "" ) if selected_script else ""
167207 time .sleep (0.1 )
168208
169- def send_and_clear (agent_script , scripts_dir ):
209+ def toggle_settings (current_visible ):
210+ return not current_visible , gr .update (visible = not current_visible )
211+
212+ def apply_settings (agent_dir , scripts_dir ):
213+ new_agent_dir = agent_dir .strip () if agent_dir and agent_dir .strip () else str (DEFAULT_AGENT_DIR )
214+ new_scripts_dir = scripts_dir .strip () if scripts_dir and scripts_dir .strip () else str (DEFAULT_TESTS_DIR )
215+
216+ agent_choices = scan_agent_scripts (new_agent_dir )
217+ scripts_choices = scan_script_dirs (new_scripts_dir )
218+
219+ return (
220+ new_agent_dir ,
221+ new_scripts_dir ,
222+ gr .update (choices = agent_choices , value = agent_choices [0 ] if agent_choices else None ),
223+ gr .update (choices = scripts_choices , value = None ),
224+ False , # settings_visible - close modal
225+ gr .update (visible = False ) # settings_modal
226+ )
227+
228+ def send_and_clear (agent_script , scripts_dir , agent_dir_state_val , scripts_dir_state_val ):
170229 if agent_script and scripts_dir :
171- # Just send the script name - backend will run from AGENT_DIR
230+ # Use configured directories
231+ agent_dir = Path (agent_dir_state_val ) if agent_dir_state_val else DEFAULT_AGENT_DIR
232+ scripts_base_dir = Path (scripts_dir_state_val ) if scripts_dir_state_val else DEFAULT_TESTS_DIR
233+
172234 # Convert directory name to full path
173235 if not Path (scripts_dir ).is_absolute ():
174- scripts_dir = str (AGENT_DIR / "tests" / scripts_dir )
236+ scripts_dir = str (scripts_base_dir / scripts_dir )
175237
176238 while not output_queue .empty ():
177239 try :
@@ -180,7 +242,8 @@ def send_and_clear(agent_script, scripts_dir):
180242 break
181243 msg = json .dumps ({
182244 "agent_script" : agent_script , # Just the filename
183- "scripts_dir" : scripts_dir
245+ "scripts_dir" : scripts_dir ,
246+ "agent_dir" : str (agent_dir ) # Send agent dir so backend knows where to run from
184247 })
185248 message_queue .put (msg )
186249 return ""
@@ -212,9 +275,29 @@ def update_script_display(selected_name, scripts_state):
212275
213276 demo .load (start_websocket )
214277
278+ settings_btn .click (
279+ toggle_settings ,
280+ inputs = [settings_visible ],
281+ outputs = [settings_visible , settings_modal ]
282+ )
283+
284+ def close_settings ():
285+ return False , gr .update (visible = False )
286+
287+ close_settings_btn .click (
288+ close_settings ,
289+ outputs = [settings_visible , settings_modal ]
290+ )
291+
292+ apply_settings_btn .click (
293+ apply_settings ,
294+ inputs = [agent_dir_input , scripts_dir_input ],
295+ outputs = [agent_dir_state , scripts_dir_state , agent_dropdown , scripts_dropdown , settings_visible , settings_modal ]
296+ )
297+
215298 run_btn .click (
216299 send_and_clear ,
217- inputs = [agent_dropdown , scripts_dropdown ],
300+ inputs = [agent_dropdown , scripts_dropdown , agent_dir_state , scripts_dir_state ],
218301 outputs = [output_logs ]
219302 ).then (
220303 process_messages ,
0 commit comments