5
5
# @Project : browser-use-webui
6
6
# @FileName: webui.py
7
7
8
+ import pdb
9
+
8
10
from dotenv import load_dotenv
9
11
10
12
load_dotenv ()
11
13
import argparse
12
14
import os
13
15
14
16
import gradio as gr
17
+ import argparse
18
+
19
+
20
+ from gradio .themes import Base , Default , Soft , Monochrome , Glass , Origin , Citrus , Ocean
21
+ import asyncio
22
+ import os , glob
15
23
from browser_use .agent .service import Agent
16
24
from browser_use .browser .browser import Browser , BrowserConfig
17
25
from browser_use .browser .context import (
26
34
from src .browser .custom_context import BrowserContextConfig
27
35
from src .controller .custom_controller import CustomController
28
36
from src .utils import utils
29
-
37
+ from src . utils . utils import update_model_dropdown
30
38
31
39
async def run_browser_agent (
32
40
agent_type ,
@@ -268,11 +276,6 @@ async def run_custom_agent(
268
276
await browser .close ()
269
277
return final_result , errors , model_actions , model_thoughts
270
278
271
-
272
- import glob
273
-
274
- from gradio .themes import Citrus , Default , Glass , Monochrome , Ocean , Origin , Soft
275
-
276
279
# Define the theme map globally
277
280
theme_map = {
278
281
"Default" : Default (),
@@ -282,6 +285,7 @@ async def run_custom_agent(
282
285
"Origin" : Origin (),
283
286
"Citrus" : Citrus (),
284
287
"Ocean" : Ocean (),
288
+ "Base" : Base ()
285
289
}
286
290
287
291
@@ -364,39 +368,39 @@ def create_ui(theme_name="Ocean"):
364
368
with gr .TabItem ("🔧 LLM Configuration" , id = 2 ):
365
369
with gr .Group ():
366
370
llm_provider = gr .Dropdown (
367
- [
368
- "anthropic" ,
369
- "openai" ,
370
- "gemini" ,
371
- "azure_openai" ,
372
- "deepseek" ,
373
- "ollama" ,
374
- ],
371
+ ["anthropic" , "openai" , "deepseek" , "gemini" , "ollama" , "azure_openai" ],
375
372
label = "LLM Provider" ,
376
- value = "openai " ,
377
- info = "Select your preferred language model provider" ,
373
+ value = "" ,
374
+ info = "Select your preferred language model provider"
378
375
)
379
- llm_model_name = gr .Textbox (
376
+ llm_model_name = gr .Dropdown (
380
377
label = "Model Name" ,
381
- value = "gpt-4o" ,
382
- info = "Specify the model to use" ,
378
+ value = "" ,
379
+ interactive = True ,
380
+ allow_custom_value = True , # Allow users to input custom model names
381
+ info = "Select a model from the dropdown or type a custom model name"
383
382
)
384
383
llm_temperature = gr .Slider (
385
384
minimum = 0.0 ,
386
385
maximum = 2.0 ,
387
386
value = 1.0 ,
388
387
step = 0.1 ,
389
388
label = "Temperature" ,
390
- info = "Controls randomness in model outputs" ,
389
+ info = "Controls randomness in model outputs"
391
390
)
392
391
with gr .Row ():
393
392
llm_base_url = gr .Textbox (
394
- label = "Base URL" , info = "API endpoint URL (if required)"
393
+ label = "Base URL" ,
394
+ value = os .getenv (f"{ llm_provider .value .upper ()} _BASE_URL " , "" ), # Default to .env value
395
+ info = "API endpoint URL (if required)"
395
396
)
396
397
llm_api_key = gr .Textbox (
397
- label = "API Key" , type = "password" , info = "Your API key"
398
+ label = "API Key" ,
399
+ type = "password" ,
400
+ value = os .getenv (f"{ llm_provider .value .upper ()} _API_KEY" , "" ), # Default to .env value
401
+ info = "Your API key (leave blank to use .env)"
398
402
)
399
-
403
+
400
404
with gr .TabItem ("🌐 Browser Settings" , id = 3 ):
401
405
with gr .Group ():
402
406
with gr .Row ():
@@ -454,7 +458,7 @@ def create_ui(theme_name="Ocean"):
454
458
run_button = gr .Button ("▶️ Run Agent" , variant = "primary" , scale = 2 )
455
459
stop_button = gr .Button ("⏹️ Stop" , variant = "stop" , scale = 1 )
456
460
457
- with gr .TabItem ("🎬 Recordings " , id = 5 ):
461
+ with gr .TabItem ("📊 Results " , id = 5 ):
458
462
recording_display = gr .Video (label = "Latest Recording" )
459
463
460
464
with gr .Group ():
@@ -477,61 +481,67 @@ def create_ui(theme_name="Ocean"):
477
481
model_thoughts_output = gr .Textbox (
478
482
label = "Model Thoughts" , lines = 3 , show_label = True
479
483
)
484
+
485
+ with gr .TabItem ("🎥 Recordings" , id = 6 ):
486
+ def list_recordings (save_recording_path ):
487
+ if not os .path .exists (save_recording_path ):
488
+ return []
489
+
490
+ # Get all video files
491
+ recordings = glob .glob (os .path .join (save_recording_path , "*.[mM][pP]4" )) + glob .glob (os .path .join (save_recording_path , "*.[wW][eE][bB][mM]" ))
492
+
493
+ # Sort recordings by creation time (oldest first)
494
+ recordings .sort (key = os .path .getctime )
495
+
496
+ # Add numbering to the recordings
497
+ numbered_recordings = []
498
+ for idx , recording in enumerate (recordings , start = 1 ):
499
+ filename = os .path .basename (recording )
500
+ numbered_recordings .append ((recording , f"{ idx } . { filename } " ))
501
+
502
+ return numbered_recordings
503
+
504
+ recordings_gallery = gr .Gallery (
505
+ label = "Recordings" ,
506
+ value = list_recordings ("./tmp/record_videos" ),
507
+ columns = 3 ,
508
+ height = "auto" ,
509
+ object_fit = "contain"
510
+ )
511
+
512
+ refresh_button = gr .Button ("🔄 Refresh Recordings" , variant = "secondary" )
513
+ refresh_button .click (
514
+ fn = list_recordings ,
515
+ inputs = save_recording_path ,
516
+ outputs = recordings_gallery
517
+ )
518
+
519
+ # Attach the callback to the LLM provider dropdown
520
+ llm_provider .change (
521
+ lambda provider , api_key , base_url : update_model_dropdown (provider , api_key , base_url ),
522
+ inputs = [llm_provider , llm_api_key , llm_base_url ],
523
+ outputs = llm_model_name
524
+ )
480
525
481
526
# Run button click handler
482
527
run_button .click (
483
528
fn = run_browser_agent ,
484
- inputs = [
485
- agent_type ,
486
- llm_provider ,
487
- llm_model_name ,
488
- llm_temperature ,
489
- llm_base_url ,
490
- llm_api_key ,
491
- use_own_browser ,
492
- headless ,
493
- disable_security ,
494
- window_w ,
495
- window_h ,
496
- save_recording_path ,
497
- task ,
498
- add_infos ,
499
- max_steps ,
500
- use_vision ,
501
- max_actions_per_step ,
502
- tool_call_in_content
503
- ],
504
- outputs = [
505
- final_result_output ,
506
- errors_output ,
507
- model_actions_output ,
508
- model_thoughts_output ,
509
- recording_display ,
510
- ],
529
+ inputs = [agent_type , llm_provider , llm_model_name , llm_temperature , llm_base_url , llm_api_key , use_own_browser , headless , disable_security , window_w , window_h , save_recording_path , task , add_infos , max_steps , use_vision , max_actions_per_step , tool_call_in_content ],
530
+ outputs = [final_result_output , errors_output , model_actions_output , model_thoughts_output , recording_display ,],
511
531
)
512
532
513
533
return demo
514
534
515
-
516
535
def main ():
517
536
parser = argparse .ArgumentParser (description = "Gradio UI for Browser Agent" )
518
- parser .add_argument (
519
- "--ip" , type = str , default = "127.0.0.1" , help = "IP address to bind to"
520
- )
537
+ parser .add_argument ("--ip" , type = str , default = "127.0.0.1" , help = "IP address to bind to" )
521
538
parser .add_argument ("--port" , type = int , default = 7788 , help = "Port to listen on" )
522
- parser .add_argument (
523
- "--theme" ,
524
- type = str ,
525
- default = "Ocean" ,
526
- choices = theme_map .keys (),
527
- help = "Theme to use for the UI" ,
528
- )
539
+ parser .add_argument ("--theme" , type = str , default = "Ocean" , choices = theme_map .keys (), help = "Theme to use for the UI" )
529
540
parser .add_argument ("--dark-mode" , action = "store_true" , help = "Enable dark mode" )
530
541
args = parser .parse_args ()
531
542
532
543
demo = create_ui (theme_name = args .theme )
533
544
demo .launch (server_name = args .ip , server_port = args .port )
534
545
535
-
536
- if __name__ == "__main__" :
546
+ if __name__ == '__main__' :
537
547
main ()
0 commit comments