From 5539a838bf97a7c24d4497fcea65ee69069e6919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20=C5=BDuni=C4=8D?= <36313686+gregpr07@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:07:50 -0800 Subject: [PATCH] update README with new example --- .vscode/settings.json | 11 ++ README.md | 100 +++++++++----- assets/web-ui.png | Bin 0 -> 24513 bytes requirements.txt | 3 +- webui.py | 297 +++++++++++++++++++++++------------------- 5 files changed, 247 insertions(+), 164 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 assets/web-ui.png diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8b09300 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.analysis.typeCheckingMode": "basic", + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + } + } +} diff --git a/README.md b/README.md index 5d6363e..9d9eb6c 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,53 @@ -# Browser-Use WebUI +Browser Use Web UI -## Background +
-This project builds upon the foundation of the [browser-use](https://github.com/browser-use/browser-use), which is designed to make websites accessible for AI agents. We have enhanced the original capabilities by providing: +[![GitHub stars](https://img.shields.io/github/stars/browser-use/web-ui?style=social)](https://github.com/browser-use/web-ui/stargazers) +[![Discord](https://img.shields.io/discord/1303749220842340412?color=7289DA&label=Discord&logo=discord&logoColor=white)](https://link.browser-use.com/discord) +[![Documentation](https://img.shields.io/badge/Documentation-📕-blue)](https://docs.browser-use.com) +[![WarmShao](https://img.shields.io/twitter/follow/warmshao?style=social)](https://x.com/warmshao) -1. **A Brand New WebUI:** We offer a comprehensive web interface that supports a wide range of `browser-use` functionalities. This UI is designed to be user-friendly and enables easy interaction with the browser agent. +This project builds upon the foundation of the [browser-use](https://github.com/browser-use/browser-use), which is designed to make websites accessible for AI agents. -2. **Expanded LLM Support:** We've integrated support for various Large Language Models (LLMs), including: Gemini, OpenAI, Azure OpenAI, Anthropic, DeepSeek, Ollama etc. And we plan to add support for even more models in the future. +We would like to officially thank [WarmShao](https://github.com/warmshao) for his contribution to this project. -3. **Custom Browser Support:** You can use your own browser with our tool, eliminating the need to re-login to sites or deal with other authentication challenges. This feature also supports high-definition screen recording. +**WebUI:** is built on Gradio and supports a most of `browser-use` functionalities. This UI is designed to be user-friendly and enables easy interaction with the browser agent. -4. **Customized Agent:** We've implemented a custom agent that enhances `browser-use` with Optimized prompts. +**Expanded LLM Support:** We've integrated support for various Large Language Models (LLMs), including: Gemini, OpenAI, Azure OpenAI, Anthropic, DeepSeek, Ollama etc. And we plan to add support for even more models in the future. - +**Custom Browser Support:** You can use your own browser with our tool, eliminating the need to re-login to sites or deal with other authentication challenges. This feature also supports high-definition screen recording. -**Changelog** -- [x] **2025/01/06:** Thanks to @richard-devbot, a New and Well-Designed WebUI is released. [Video tutorial demo](https://github.com/warmshao/browser-use-webui/issues/1#issuecomment-2573393113). + +## Installation Guide -## Environment Installation +Read the [quickstart guide](https://docs.browser-use.com/quickstart#prepare-the-environment) or follow the steps below to get started. -1. **Python Version:** Ensure you have Python 3.11 or higher installed. -2. **Install `browser-use`:** - ```bash - pip install browser-use - ``` -3. **Install Playwright:** - ```bash - playwright install - ``` -4. **Install Dependencies:** - ```bash - pip install -r requirements.txt - ``` -5. **Configure Environment Variables:** - - Copy `.env.example` to `.env` and set your environment variables, including API keys for the LLM. - - **If using your own browser:** - - Set `CHROME_PATH` to the executable path of your browser (e.g., `C:\Program Files\Google\Chrome\Application\chrome.exe` on Windows). - - Set `CHROME_USER_DATA` to the user data directory of your browser (e.g.,`C:\Users\\AppData\Local\Google\Chrome\User Data`). +> Python 3.11 or higher is required. + +First, we recommend using [uv](https://docs.astral.sh/uv/) to setup the Python environment. + +```bash +uv venv --python 3.11 +``` + +and activate it with: + +```bash +source .venv/bin/activate +``` + +Install the dependencies: + +```bash +uv pip install -r requirements.txt +``` + +Then install playwright: + +```bash +playwright install +``` ## Usage @@ -50,3 +60,35 @@ This project builds upon the foundation of the [browser-use](https://github.com/ - Close all chrome windows - Open the WebUI in a non-Chrome browser, such as Firefox or Edge. This is important because the persistent browser context will use the Chrome data when running the agent. - Check the "Use Own Browser" option within the Browser Settings. + +## (Optional) Configure Environment Variables + +Copy `.env.example` to `.env` and set your environment variables, including API keys for the LLM. With + +```bash +cp .env.example .env +``` + +**If using your own browser:** - Set `CHROME_PATH` to the executable path of your browser and `CHROME_USER_DATA` to the user data directory of your browser. + +You can just copy examples down below to your `.env` file. + +### Windows + +```env +CHROME_PATH="C:\Program Files\Google\Chrome\Application\chrome.exe" +CHROME_USER_DATA="C:\Users\YourUsername\AppData\Local\Google\Chrome\User Data" +``` + +> Note: Replace `YourUsername` with your actual Windows username for Windows systems. + +### Mac + +```env +CHROME_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" +CHROME_USER_DATA="~/Library/Application Support/Google/Chrome/Profile 1" +``` + +## Changelog + +- [x] **2025/01/06:** Thanks to @richard-devbot, a New and Well-Designed WebUI is released. [Video tutorial demo](https://github.com/warmshao/browser-use-webui/issues/1#issuecomment-2573393113). diff --git a/assets/web-ui.png b/assets/web-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..383fffc370d7a64dd7037fd37220a9ba79382a70 GIT binary patch literal 24513 zcmeFZ_g7O})Gizmq*p;h2Pskn0v049Rp}toMOqAqQq|DARK-XyQ9wkB5IRT;9i(>< z5FsEUy(vYc-?j0)_q)G+|G>M)IAcUk_9}DD^2}$>m6y6Ys zNqvAKgTX9~)Nd&oc$``xY8suh9}!<^KPYwjMp8FS@`>yb_2+Z2GV{}>@5^D2PU((c zO(U~&9Q|?*_sL4kH0s#BUgkoqjmWbX&Di*vXkf1 zgyoI@`TQt=hvt7iYcm9&`tS35%0R^b{P~I^1GFLN2@r&K`0wc_RfP6(^1S7tIQ`#8 zg|non|31PV1^(~g|0|Xg3H-0N{I3!HXA1x8TmJvO;JPccj0IWu4DrK|z60 z{n1RMywTJq%kifC@#gyR>eTVk&hg&2uV4QTtvesB->dV}wtj79X?eWAb7)+zvp03D zRWI)3w?3*_ANua5cljRO$pQSj?7usIyaWp<8x3Qv^E)K$9sQnZ^ACfECOlE*_E>19 zO-f2Srjw)S`Z7pvy0<-W?3K6uG)azO$lr$e^Izal0AZlv?NX3LVq#)$zowtLI(JmZ z4k>!CAt-`prpxs2*5c5@^X#;(<)1CD&J#yQMwb4)So97=2dmfo2haTcCe6*v@|5T* zUgL9?9;!wKb1}sXF#eo&I`d(}H)r`rR$g>4s=T72+PKhy^3F+MYG7jwJ{8a^@FnWI zCJAD3ZQz3(N9=cHWZMG8v+BGc7oUzDqtVaW)z#I+-rfk;f5FUN0azYed@mPeeR4{Q zSY30Q?;^#9tuI_;I?>0nV!K~+=Y56q)KPUT!#_{O1>)KxEu>G4_l}bo(syDQ=|=h( zMKgjc5uB{?0U!iQK4u!Fjb;gRaMQ^krX4a`W4fysqp-ZmftRbm`J@7>3@(u zK0bcdr!p)kD9Df}i;S{9V@6X+7g;ZBrXskyzFv3;7dx5R1PNv~$tLAu^1Ci4w^mv2 zLLzhX=4ygSvW}}7JLeYbC?RGv5>q(P zlpOyJ`+<)JcIj3VL!o8;xd2u*g0Ij1pZwfQ&l9orOg?Y!s0d`<_=g&bXaG(qf?d3L zF*oo)4~a9_+^IjV9iNFvlZqDIsiaZ8>-NV9oGg z)X6CC<)Bf7p1(+xuylhgrLr2UpaGlv{{4Nf4(#C@ra;f*Bj25svC@CapREd%A)GPL z6X)U&WGgn6&$qurtafiT7c(I-UZmlp`FFb8K*1Gwl@Rn87Z-y%YAhYXQBsidWJ)_G zaPUg+;zgl7VBGB>%&@?x9_i7C$E2NCfJ=fEW4= zTdY%N#o=-uUmP0S9^n_6S;C% zkQ+Ctmq&@fQVFhKBI`5~W5!!8#MI@ba_r_a>AB_d3_l9| z+v_W^M(ib`72XxErg*fq_32BLaWpbmS#0>n^dlb^5h?@V8utRi>Kt?FVSq6%-a8b9!}Ltv_D(;~$-LrSSs? z+dzPh6+3ruP?KwIu8a_lv1ZmQ>y<@okjJd7yK=O}KE1P8rzcDko60vI6dVH!@UI$c z5tth4nB9-+Lc0Bil!HrE6$nP1>xb~xTZe8!hfoFx5Kf#K<7&uy>w^L08LK+u z{L9$iYOuW6x}YxK>Pi}Q@D>baatAt!7!7o^9!!nbDkY;KX4U`c(}A)AZvVK3$blRF zMvk&$>-EP@+^70$Nbkz~9enN4KEa6DVvcXUkbnPtc+KO3gWb(JWdx;x^}*e6 zSyoR0tKUCsCz$2j2MA{mVEy1v?jX(1cYfAa2~aVZL0Fe>U*3q*)XgevhJoCSAiAcPCZO^<)nX*CEtlWvG%W>-{J1 z7CBZj1t^meVv%AfCg~Ion0SF--%RF>!9Rc8q$@PihyI_P7l{zLAU}}6$;IVHPEKxa zKe#))Ip0Tu;LCYp27gU@^4%E-4Lmno{L^nI4ML0O(SfJCkIzII|Nb(FW4XU?E}Au6 zFG-n~yEZ7P0sjXOT6uw=Ii0KfT_>%+1#w#m!5Z>K?*)&!C6k{zMb`FRBSQmtq(LU; zpOgEf0uJy@oDMyueK+I^?X{7s8E3UZR&T^%7kB@xj@xlC4R!pM-1(Z5Fd})fC<6$A zoW>94WR&$@fI_qmf|9L_XMY|nKlir|hFogoZu#znISP-}J@HrpBM14XVP`ghnL0W3 zy}M~|cWJQNeVqUV%&n~CCB2G3IA$|E>fEhsx$809T~+wH z=XAjDpEj>F(%tXZugy;>8Xk$o`KQkBsVQ_m*}!G*_v*Os5 z_6hMOn1n6+S-9kEI3Hkzdg}*7ahrazZ5@)!GW4)7)>oKdG}%o_+JzRiO9=)36kn7m z1nDo!`~2;%^*^3$j#d(>IW%7E(O;9W&sKunc2E1=Q}sF|8Is)hBS>EqkWI1{+<}jI z`HSl7r+kkNs$PoUT=a{!8l0T8xE!+MviN3bd)u|Ggt9{N8qh`XRLVm-sTN3rd3|qD z?B@(wWA%=RPAG|z|` z7js|SZh)T3zn1rJ254gvfDo`jZ1{9p~qoG7Vtk0~3m!B$xm53sys z9atqif@)S=ukxn6{JOZfxRD|*N8nN%`a)ycl0624DGdtitU|3tSOp?x-e9K0XT44o zW_ST84y{Sk{TWTVMr-E5FnaEDcjL4aWz%0m2yYnoK}HEt8~kK2B+DyWPO+q_oAdqm z-!8$A$CKL@T3c1fp+!xkg1~#l%HBRV*CB@&$6Og15phnEO1R}4mQMpFnEn#a+*m9QgI0noO>Zf@1i&YWHONhJHEb6D#uSKB zHt-ke7heWIewS4=43*Gt`10*LSZatuUr zc8(^`M6Thz;3^EEVfZ39>{qrvGosrw^U<6W73G5bWB#l*a1ITV|%}p~V zRhlmu03?Dy4m?zS!1Hn1g(aN#Dn%34w!m@O=Okp%Uj$;JPGHc=d@#nOj2H{0Y`o-3 zbDM+%N+#(fHARw*Xqw@XpOq<_K52p_f+t}fumXq|GOcg}DrXKZE^%>EpkSqJAzzv( z!kM*)IOjkn^9sJs>>_usQK_mnP<#ah^3{4Mw3GP07 zTRHoGf1@jO=8%y{X=r^7&8#3CQX8S-G?fsV)N%Zm22CmEvVf~FbjM{f&+j9;CmK^+ zg5MDS=6xlGzJC4MNk`vREJv!-fm5!K&JY^o*Apl@MCB-xQ#N*+-?B8N5F8gxQH6v# zvYj3Suxy|R3YXJt6Nqs1*xPz6T_GHiBBF@HK6=|^HlOagh&Yih`XnA}nfJfjcl;e2 z8+$v%YR^h7L>#H#@mBWcP4m~!O#_)LEbIMgVqg^6-`c5 zsPyQ(s}r=FfVO|@kYVF{9#Mr%%7Dq(>Q2`U%3Msb8abrhCrwN!;RHzB2wgE3wR(B@ zn%;_&>Yv$(wvn%jxPx6xr1-M-Hpa}{+!ceXo&j)nsIy++Xt&QlVIP0RPDbLd!GsDy zhD5>qL}6eB8&iThS4k`W#&ZxlcB0RzqLs=QTJ{8}~j^*I*NiM^LF4Aog6SO!A~vzslr(R5l_A53}&rH_z!h-bUX+Y06H!yg%<#<#%wb} z`142Q0fRx*BdyldPnp{h2+V8By*I_xW!=N4pmYH|9()=pwQQj9NORI8YHBO3pBYa+woiz>eYJio%It@=l+u0LOW% zm1~Ge_@LHj_gQ%~gL}N=!E`e@FD}}8+=$+eKksS_vWJa5T@>qtaD2O2$-3$BcW>L? zSUWd8+U2Cm6wn5xQ8XeU{b|*P8IR;{FQ9=P`A`TZFnlp7k$V)UYtRxcG!Rw}B7m&d z^mX1(X_1eYE3`3T@MmV&-6BsOQ#GoP8BIp$xHE5%&dzIih|1671{r}7dH2sW|Bb!Y`RW9 zMxptixI;%$ltw+F?-C>m2{s-%OqW#p|n>%6u*y@2-&xB=zN&u6E%q6TRn zcKvx7;(1D=Flux9{IjJT3F5NQCh8ySsyJ%W*BfqUpuFWTg>>H z>cmEQ&cM`u_uhspK)!pv1(H?t9os689^bUd7d&|8=5}rO=G|WPUGGhnd#`q-n{YdF zKn_LL5GAIVd0pzu{kLfMpbq72m;G$ECkwz%a_O55{PwS(iZ7-fWsL+JQ2)(@4~Erq zaw;H8uTC>uJ;Q=Na=@6K9~Cx>UQKdxsUq(%CV6dm+PF5D=b6ODxO07efei3noXfoX zb(Xo5Du$KLIy{2%cSh37D8S^$P=>66Fi7+E_xJzO^c8^o#S6C9KVLI+9}L=Dn0b!Z zxNDc~Bv#RJ&12on_eU-ib?3YtgIQ!`q@i0GJ7`agSA=oE&ToZ zhZf;sRjdIy1f$YFdu2i=FnP{am)WguSkgTnSKwJrbvD2E|}T3Y5axFC^K z&x&(=e`lTBuxN5(V&&0jGAh)Sx(Pd`XdTf<+^9->p!%BEI_0N@7;msZ@T;n%yuOvb z&QrK`%Fkdk4XG#jcc{=q8;*XHL7iH!hFcU_SMnP3XRIMlOnvqq_4>yB#3hQm2j8JCuY-srg#W4 z_M$b&$&(7Z$Y;Hek5*HDZMWTJ=N z1)`DheK%T*<&1wLKOtx@(6h7=Tx9F)&;kB1w!uL#&AMF?!F><#Q z&WdEY)E;!y>1?}0ylN7$QW}iHyGgctrP}c;glhp9x}TKqU4?1 zbGE_e_*U!Xa97(wIUBAFJ3K%IV7U}dgNib7-EQ)97Hld%<_sEfT%BFApBz5jVN=A{di^~_ovWg1ndjl7jucL4dk=$etO6s@= zg~M&$Twb}dWy(!p*NS+a*8C3blJ_QEx8^4h@0I8XHma2hO*NtM9IwPo*XaEqSN4`S zW%DMWo59K)BAH*TAoFb>x3D$cFYu2C2UEi23w7|GTSyG z^ry}Pb!^~()bW-(YvdI-3PEQ(W2Y7g(xK42>FP7}Sqagc`sX=IvjAIF4bs%Yy7?W$ z3i@WY^yLm;8qYdz0_Jvk7Zb9n`)QBax38b%`Xz_|R*mo@-W1DfIj1NiYB?&#pp3d- z3KHQ!A=MOXn$$zUC+vyV=cM!%YO37WYf!x{(>o{RWb2)ASql_)R#sf{!i-vLXnngK z92{u;BCaMZ2w;w;L8wm}M}&~>s2VF$nm|ENFO zWF{FBzTiw6DL{trq^}&JwC(Qe+uD;fW}zmqPD}2L_vHukLBjG=4m}%6hx`c#;O3F? zaWaNfHi3z%wz5*_rqk2M!Kkd*RR`>7s?Fiz5^(SexzH@uQ|=*v$h8cyY8Cg&T(I1B zTy+SNyfKYP4Xznn{P=;{o0;QHQ*zoz-qTddmdekF-VogWlu3?CkmC9U*iY}ny)uUG zhG0}{;_nx0H~cTPeOl82Li(fzInt3xWp!JeOIQ1NHu}7}zHZK5s4bOWIHO~i(0e@5 zujG%^$m!NAwnft*ezSK*wG5c;oG{_EtH2YncXIGX3Xq}(DFx@z&O`e;z&lB6ds+wq($P`37A}&+-#q%GV8V zyYDy2do&^Q=U&<79fHy&frhubhUJzK6NF-C>^c_8e4)(QSxMCxGj_NwiT^~-ui}CZXyrtXtX=j1f-v3ZV zqA#ys)@=5ZEk?H;@a=N#?X?HTn8Jg+LNT6k80^QhYM4WMC;d& zs}JOMZfaI@uT?|6R1Gmuqz*I`Gnv(14S!BPm-qqQB1tcd7vEiVT=LxbsV*JHJMxr} zOS8C-_REVvD4WbjAXs0<7255=x^bxZB!AVi{=ns-8YUynznS#qTfBLeAzwG9rfBX% zoF#Cxs|g>xY=yvFe_4R`m}G-f(^J4B@h{n+%47kxQaF}X@s#qL%in)}MYQ!34eR75 z>(?1B2jltD{4zBbKKxM20L6HUtd)_N`ip5Hq}=!?zHe3K2aDGgw!UJF3Mh9sOX?CB zUJS4Ro+2O_iNQEa9cwe^qNzDV_q zC}2lpUwXZ;3Z4k5^E<6jNH?WOM@j!Vm z^>=ircSNfRa~QA-D`*Ql>1>A{`+~xnD-!B20fL}xNmIN6@04mbh+FbQ9>8H#Y4?(}B-u#|nd3?L-$d(KUb z&?!Vir=ZsI!-q=QaO!~#t4Ki_X)iOoXCAP_P;rl=yrLT_syJZWYm!-O<=1vRCFZNB z$krUf!ikM_iAA@Xk}%aZH7g!9a-^{gMr4LfM)D72AsRySq^lvCL90w!N?#Vi^s)_) zyZ{QAT=o#!YDO2ZMAF9ktlK_MltgmN{$ZsyN9& z`pCnv(HuUl_@@^&mqik{W!*<3@`eD76cKglfrVCsJ+!Z9{Si z?y$v=nh(!3sfk0!2C7Dr{wA07Esp_Tsvf;1o#3U5C^ zOF^}UUS+cPCD>|bcHrvjYC+7*4B8y!5ydPuzwm4ew~gE~t$*yDmcI6(4yRkvCWmn*d-q@u=g*uGu_6K}{$9eLzDoclT1ZUU&4=C@0AiBe# zPmY>fIuk2?0h8L5FiD#w?XIhcm|jbO|LCpNMbaB?^lw5zxf|+c4M)n$OBi}cgMn@M z70O8ZL9$1_bJdtUFw9IolrHE1|LXuG>Q0a74{7u*o##zK`^0(T1U_Y)8?U50J@oMo zXGNFi0&w$0CJ5-oQ&>P55kE-db#Fq)B*h(5+Ho0Q!Yy*Mh)et%QLedjH;z zFM5M#QjI?=;er>k*+6ggYerpFAcCGUn1;wQYfLsFNmT=_)IInO+T4Ib`zNOF>Nz`_ zD*3nit#7n%j@UmIAyZ!ymG{n^5wP#UsezFblpry5oSvg{c9?Pjz`v1WQ0Bd!Dy=!H zvJhfs>5PqyiN3Vv-wzB=N#CAKi(Dr?&Gldj(a5pFB+#0qDr*E%L2qiTTE}NGC68(V=QcK~nID;`Rg+M2-2l8twSJ1byYNg{jUN^`-KxlfxP3hGw`8K`jQAV}P zO;+TsU;P7|`H?D}GWKhXNEX6JENZRqr`MNqK=HqJmx6e}fJ`W_Q-Z2G8rWMO!t+`* zLU_OqBQ@-IY?)%iuD|D?{Ym|e?Y;VW-)f4N4RPH%jmF|=6-hB4pGX}(2;gaF<97*M(BXnQ%KVhmri zqvS+E*(5J<+k|rl=ckr!)7iAfF<(rjh|AB>;2#h7?9Yji+A3+=vO<#trw&Z=2s{%Q@UMWh_qSacE&=c z7uzd?d)4T#y0@z`WI3|5IuoJlD{$mcTlnSN5yK`memwA;OZa}4&}tK-R_%lj(+-gmj64z4NwWgt9^V83JG)~nu^;{bC;4a z+|MRLdN*;9Q808;Zs+h4R9}qCq6;ygQ2)dg{N_eR6dfnQxP$Oe(l{1Iw= z5z)DG=Y~n3i#T3NkcW7#=5?RroJh>nM-!PXw#lzwD>skbuq5lga(w{QO=>^x#2Ak# zgsjoV6YgQtn&vT_cJ~2s0eYAI6FDMXBZYsEGHY9jWmQcvThAB=Vg;Sk<+k1J0!5r; z_*;xy@XP&R641s;$k8GWr)f0q$u$ab%>l-MbM+gbckumCSuj8VQr9;I%V%Qg*o*l0 z_xEikqk4<;5T6}I$Wg5()jPX2=wRiwvrca|zNYLYU)}!tF*WB)&jT8kz|RGWj!=eX z98v~GYG8u$ZY`If=WXh!GK~Py+q~cqwP$&{`%{3}9ZqoNr+rY1d6v;la0`AKDJhp8 z{mm#pH)b2WuHXt0l0mYvM}Q1A4sk+UGHm5D6fWWd5dYU@Ox{qn+ih0Dd(WvgZfHH5 zZ?aaL5Zg!wK!r`1JG(4Cx?fhHF#@&U3?G7y0>MNEt$1l+*C5m)pf3rdg_iCZ&ASUf zetdvB1;2X_PtMgpUMTwPTPTcX_E-nQU0Tqor4wP!E%g@-@xPy`tU8V-=2Alf*G&N? z)BS$Xgr8_-LLj|kQR8v{ zSxsgl6XH{rM$t0p z@O=K_I0p9l%lrhe5YU0Kyk?Je#3brS>z_e!m6&rWOXId5_*WDT`d05ljbZ-=0X>8T zxk}P-Q$RXvg)2ia*~fXQ0I@vD2$i&QGkzRF_1%DT5LO8uNn`H@t!lOL-6{O=Yg>|c z1-`oiy1Q{l#ErCyKv)Xh>hG&8E*_xGH&un3e+@9A0R5x?|fK%=0sAsBmpc@ z(Aqd=3rt{x+od&qYA( z8`TD9k5&2+84k*Asq+4E9%=Eji^>RjQ|jIII{G%*5zq)DT|vr#RsE3##Y?*iGStQt zEs=3H&HN4%6Dv~4C9zN1Mim$4)&;}|dMKdQQ^6=FVHWIFsk!pL#0xWhBn)|BEBmA$ zoR({zJbN}sE1VK3;$jKk!xTFVBb%^KE&RHlO1Dbh7ofP@R#y+s1{%nU6FSCry+?Tn zE}`3%P$$MC`QxJV({s+LiCry_~;X)tO;bwuUQ zu6Ym-XiX$Wfqp34b*k7txvlvJV_6ol6|Q8g1io%`kQ~|}$$6;hb6`!FQG2&wVczVTmLw3Ql$trk2Ga7MiZSDKM1Gb?ZBq`DrBGcFB zuDT{hq_VJsI|<)t9bYuG!PBBseMfSrBrz;l*Wh~+f86&W`J$|311|s;kOqKFxFVN@ z;ldF@g+j1(&w-LYv-$->%NLYu+4H+P;me|ANyQOi)f!)^A9Mz8_w{~MK7TKi&HjqY zQ`gRd>+({@s_>Ade-8EVH(GW2)UswjInafZHaKaQinuT~`%8oH6jCorMShyiqeE|J z>GScDM(9vvtJn2C(20F7vd2$0MpF3a#5F@kD?CrP?)iH&Qwh`Rj zpKhkZwR+fJV&TkbSVJ6Eg(QISlk7R+ERTTrdcHJj`YyY6KQ&$qzU-z-<3+^)$cN~# z9+P26QFR~rwDvhsb4S(A&&0yoa@=Srn;JYI9BO2EBau~<0@e>TyTMJUoLIPi)p%{= z*}Du{lu-u{~e>u^#bS!JE>0JsD)$0|FaYU;**?EJ-Z#*!HNc}}Q6DTG+ zYF8YrPl7rdpE zPurs^d27bUd5CVudhV`?ma?2``a|rpv}`Zvvoi*n3O0B4*Q2t&IiEBCBbE@JZqP+K z`d0RerjW9vBOB1gq?-t-1c2C`bXa;ByX}|W|Jb=5CfS=l9E6yve=a*bK7u)KBJx@V zFgIKUgNKDplKDC1S@(~2oXYzCfDRNoSb!Vk@&h)kJ}RZzt|=XxlsFEtLLJCxSzxgz ztvW`T=)q+Anvg0rhKHS3ue&ikT=*>y-0^ylgubgWuDa4Fr_%N_uWa&w8lpghXmG#I z!f4+?%yoba_PzdObP?vtO@!@=bi=$KtPb3vVHwxbyiwkUX( z&o8+@ChyY-;h8|XN#Wz`}nh_hiQbadAc9qrpbuCb$Bmmt~kt5@AeD+xj6qhrgkopL; zl!!26bFr#Y=jgbyOg+jwWynGe9L+1>Zwm)+?g)aj3@HD%cG;r07bx^+Usqw*8ACm5 zm|qN0W(~4?p$m&SE@BGMoG9G0L!xZCegK?6A?d<6eS%BsvEseCjY4WE>4R2 zj>=-2bvv&d*%Ty#&tE=Qoo$Fr4AJF=DeOZ)Pk|*|R=>vAB1eFL=$^6e$vUf*$; zFEGy$$xqQ7=lp(>rVjfwkN)spYh`QR@}rFGXFa`g*?$DDUsB9ucmvKA^bYt8%fFij;aa!``TK+;;Uk+vs3P^ ztoP%4DdWSvK_upUA|D^#7Kr$S|MnsoTJ648ukC;C_qoBN?Hh&)0`ye5wTkhGZgBah zjmUrfQ+4&Y;gwAjQvLo>TI#g|Q}Pfy z^UGF3ES6i3a$cFXEZ1+c{wI1KmOzBQTz3vU_$8=e#LW1Kv|sBeSGUS85gr{$1|7_` zEFGV+xRQp*twJr_(Y~RQO;-=E;g)EZHN9R;mz#j^G`^jwLnj<6 zbKF=sdp*a6JNJp!@*GY3v6>P>%uE8qm3uCDfJglUR@F{?;93SRC^^nY7s7s&Z{b>qC}IT_A2m&z^MxxEH(@#VVEa&di(lr ze0KxR&|KjD{rjAs@*aH6{pR_dS0v(Ao|F_5EZ`}0)G!g|PxBtR!t~U+EPHhoy(k`6!guG1+G;MmwZ#5{9x(aFVGDOmy(aq%R=72i?mPRCLHrI&NhDFxu zb0ULO;D*cz9ih%Rds1_6W#dB_@!I9}UZ2V}QhTMrM_bDMEQQ1`&(x&bgn#6A=Gx=v z<=L3(CIgx}@o5__0I5!T?<)bKp1U@BUmIlFuRNl@grQ7vrAMMryDrrID4;&zQE;Iv znQWPAo^I~)tfzOLju1NKxQ%Tm(Y}Fj2K7PjxkO8`2B*aP7`vHfEF;R?fW|j7y;BdG zsn%#IiX7HZ37tNSg-ff4MWth^I=BEa0HzVEvEf`>U5$5;KNN+CpE)Tb)mPR1j3<)o zHD%|cxc(4li(QRnysK|kntKMNcaXzuq5b4+>!;7l4r}_k#M~u{X2RER-$Zs|+MxM3 zgBBoVllyPFU!dmff>Afd;cwTk&niA>LTEZk9`gCNU!>Pt8KoTmNd190tOIB7vs{FcB$3`4h`>WVe-r}j)$&-UEM z0W~ewVAee|=|8Tv*qLW|63pxxWtEX}wMD((p;vxyn9MnS{)5$uDKm1xsG{NBT~A~4 zdU27Cs=Ov&Hmw}H^BKVP-A?X$X5Lj_Dk^o35@uEV&(4@7MP&29bvbjhB}`fS2yFm>^2+Qt6rlL+^hiHNLNvm#GRO3Mg`dMTGFXYJwbNHM zJXq7a*}r3uvdC+`CY+NQd;0;WAv>Rkey>e@VZ86h!SIqhum8VoQ_<&$lCiI)`&(6H z4A2NC{6tO01To(eHRMuZd;Qrg;XuJ}CAHQI_w(8?b(<$2;oG<6!j}9{)JE0d?%aMK z&O6fOBigKL(r8UKE?SM!))wFU)X^b^?G**UvW9rn&EslGYp6bVe$?AvZ(x>7KB05v(^_y1tJlQD~7U(+ow|SDHX|mFxUvxVT-85s+rm)29at> zWxap)F`_1|@~8B-4TciasCg(xbBv&9C@PZdS7}iz?>qGh`7|ki8E`U2Z9Y)qV?D00 z_Jz$=O!@8az%#}T>USR9E+ct%ylF78qq(v{p^QIR&)1kQIU<;Q9wdWU(%=OpW@i$c zOo2JX|Jm}y8Ovlur9OR0zg2P#)Iz5WHzBw+R|E<;V#CZScM{;C_XgD<1upmyOv3a< zJrSE3qP)f)u(+Kb3%9gXcvt=Ce${C`5?{i_qP1t7cn+t01`h3yrAx<7y|vbvIvFsS z7Sy%fK(X;lmaIHHe{%f-{ifdM&dNZ*^cH-m%k5-pmATEO{1mjW|H2KF#tHm00DVs0-wkv|LRv>)vx&%SHi5o5uX;~{Ufh>TCo z{Bcc#V)8N_EPL_aPlWsu^55;UBvcvJX3U?u7fz#@5cW!6&ZMSLHlW+B!0b|QbDB$F z4uN+6`w+)OL~L2bry|v+vy|np#bzE1p0ZbeR-nn~DXgcfW*oj$qJ6d~-;J~5k0y_6 zdsFv(U+Vecdkd;1S@<(~b<*3u7EL-ki|4L{0UCg^Nnxk(z4~^sJ zB+bbUJ_RaXj64ng;9Te6WJb02cQyi~?Oo`c+U`9Vjm_x| z8519q)7;o!ua^*~DgC_cvgXM|+S8s#}y+~GPh5^auvH6Pki@Lw=uC8T!?^dNo zw_Wfv4m6tLY&#|I^wd^Izxe(vy?8x-UGp0qYfi~lbQU61z>;rtc(4ilqD&XG7FUQ7 zej7!8G5+{BF-+BzqIrux2k#cPXq)yiAVE6nB{Dx>Y&87M1WobQ zvhe-~%R3-hzIW(OsePOK!_w%`;7^PgN{bCRu+etcIR8k38wm_5Q=D#BPN;fy@lg41 zrzutQtw4luRK})y3%R4`D;Z0DvIiUPV%uqG(|~5P1dnd5?aLBkx)oa=G<&BhV?RB4 zt;2v?~bzPt>#PU8`-n||=Ugq-J;a)FV6W&7&YxoOY#g8;f)2)b;zLOit zLMUXu4Fbp{xQ^&zDDXapPI7E*cRQ-&&!+D6bOrpGz_kYjrzWST_@O*~0U2sz}Mvw#H#b`*&XSzcpjuT<2U`T1UfRJpWJtm5Qd<<@_>)mXg}z z_>`yW@jJ?od2MX@@k%f{p3jBcr!HE8$vJREZZ8> zD_}io!r}@bO>Lp8cJG^-6Eayg?z6`- z?{ZK!^TYLaPdyIr1f|T}Lba%i&dKxheJ}D4>edl)$BuP_#TQ!ES1QikAX}hlPTrew z5KgJhk|X~)5sUf41TpVoYPwSME!g4o(AwJC;TQZwFZkPNwI}ks6_X%QBxQcpQKe-I z>bXu(N&l1q+`c&O!n8J^Q2=-R>R{JjkY2S9+^aA*&RR?U#UPBIU2Sz0q-dTb~!hIbybz4!$RAk}kCoI>d@3U=JTSFtCbhgsG?>t?U>1Nmw^jWsX?&+jg z_zTwKpRe}_bFxmRtm#cNV#3T|~O217TLAd!T)Y^e`CPt%LE)}h-ndSF1;;5U}sp<^JC!rh4o0sJ|;@iw)LsxbCw z;^^pT$&Vc#is;@7wgNXMPnuHILc$PYq^Jwd*;)NVOF{X5X3)J;v#l{>o|V4p?$n^kpZOHlai4#Z5KQ}y0kTgzxJ~HF=_g9FA=@Bu#nv$8ER;= zYMXR1nE&fjX_<4I@t_|-wRZ~KPUS?

V%v-@MWV-Bj);j&%&&-xL8qccMQU&g`T2 z`BByP1ch!uZHh1qX}ox>KIbF)?Gd$;)X}_~=0bT1sH)_@^24SneW~0FOcj^i87k~@ zs1JV8FF>9(=q@_2`_zMVc~15Wticz;nhF~VSxSmR&NLOEKr%8_pvfaR69bCbM)ePU zWQ^t}M!+w%a87&*#!Q%nqzE`9xl)H@ppza+km$ZQg@>Ju^s&s+{d zclEh^{YGt`hh`Y}AjZIH)dXFkH@UF`u@~cd(z!YjH&6PWp_b zy$i9Gf7Y+6MJKw5-*@MVV#L0>hr~Q$6tskJDMKmYCvYj$Fe+@)|KPXkDBcww|K;op zR*Ui(aK#nNG_C3qyj!)@hIOJoHm#>@&Uj+uJz*}t!K-M=+tF6-xSHA3i=2>u$o>;E z=f{e(?3Iz}o7c>q83p3%MS*J|&~GEq2+_Y2WtLT&DsBjcu!trV)QI4Ysjx+qwFpto41`dE@!)qod+sbh!i%wV~oonbDo+ zcaQG(cCiX%QiEE`tjN^9Ti@=`L?NM9a$m4|fD)o*`Y=bv#P5w_i*ZvRXw#MHa(o8!Isiw<> zGWM1d?TW~tkxfRFq!cr5rIW3~Bx%Ow$doh`ns>d=TI+eA^*rD2H?r-#Mfu#a#o5-QHTVX1y6vWy zk|Da35xoAW=lP2O7FFV8Un8QD{dzNWs;m#wqhd0A7z((ifScv@JWqG!$ih=eQ3 zR$3Sk*{HadA0i45c^`gM!m^kf$5ZrLr;P~1)ERXLv&9Z{uCG)c&=K@!wT;#Ri^7x` z5!Z-_#~u>V2ZsSg;tkb013D*zhd#EKH%zz^jdFQFJ5JVf89j@tzO#wHJ$kWq1@=@f z_B7pQ(k>9~u3EwGW8wGg)VE<9>IQ?r)oeo9_AAXvb-9 zw)kgb^{tf1puEPjAzAMmDT77L#_?ribB-o=Mf5n?*{AJ40%sZ_HE{Xv?B%%xp(Z0C zKZmsTQ}>&ue#woQiC;dLDi*0>4E#6Yq$(ShG-CDnEAhI$!w>fg@#e-9RDs>;74?v} z*=OKujyWk@XKH@fvYy;S?NW7!CfU#LN{5HTB&-wm&jowPsj12Ic-*f}UbTP|_-lJD zK5QnSRV$ZfOy5>@q(L$Sg>f0ckNTrCV_pb66>72jmS9*oS~zKjv1$5_vj-r%+A<^1 zR8DQsb#J}uu?fH3nCLk?F5Yu)2^N}WMyuAu)pbYLR@3#4wh08geCQgp-ek(^1#Tk) zVH=Yg9=fibN!D%7PPyizC(~|%d{2Hj{~j|~1It0`M<7f=3!=GR)Z7|Hgw&xGCBu7E zWcFa)S(?|$bvYqSQeJT8kb9KVE@%8@*zpGySoyenHVc8_czfNk?vqn^CXyki!g`CJXlS&SaMIcaF` zbzkR2?quDQX=?`@BOwQubwr+3cU@O9+8k+h@-I zB@Qa_N+^_L2FJAz@39#_4C$CAQeq}Wx3p!Gc@))JcGG(<;Pd-H1mT-%%`W6OOOky2 zLO1Fg3H^rG^jkHNdOnoQk-F2`rTGO%uuGR0AmB0_{*c#l+;-RGscYZ%G|Zu{yTGF{ zKouZslXcw25*Liw~jpd(MCo+rVe=1K1(BVF=AXzU=FU#C93K`=3=PSk~ zCs7p$wPv)0aB$)+G;`_oHNTi(tV3v+b{=OFw&2nyd2bKS>dY$7@plLH0$BQ`TOhl3p0hSh{`xFCNq{H4H z>n?mZ0^%#@x{Z|6eltP$T{~bR4*aYVGi@PFdi-G9q#af@&q>>;+gUVF7cLq z?&)^ad(jVlJc(ir<)02X0e_Mgr|le>u5a(yh*UuGAKoz(Ego3YX{@~Cn-me?g7)ai zO4V^^4Oc}#l;Bwmg%G@DZ-32;?mH8oJE^KcK1I{I`(>n6Cx2qBE5#EdpWA+|x1Rj| zLwpuQ%UlQ5_~xO{Aq@~Jxup#@hDVVmOfl6;W=QY|ikKySGPG==YItI)qW#h}br4tb z@gQF9`{)j zxtqUoQ6I#+S5v8#ist2xoE*;MBTiQa0bQr^7N?N8=cf@KwYRq~B~)dTvGkmpSB?fk zFIy|;pL#@6jH{&*Xj29n!L;=R7AKW?icteG{+q;w3I3tCO*;|q0Fa>;D06#-GVMm_ zl5`LwY=xTN#?vVa68tBEgf*L$_Y`ZP6?qUx86o+5&#*Ua0lc~pp=($8-tQf|3oE0b zwHk=6Zz5(qIpbux)Q6$YPT`K#I%Hxa6x$= zLm5=h-?)cI!EWTa)2e5%#>gZ_0zFXzb5o#Z4tXK(>nUHY6m45Z0&bKzCeT&=Y+xT_ zG4nIiKT~Jp?imV-%gV?Cv-ARv`emKu$3-T`0!r4D7(sSKL-n9oRAv6#E%cB^AC=$% zPT7AfTQQ#PdB>nCQGMa?zWzSGQ)?aQR8`iPkFzQgGnh@9f0j^6)=25oE0mAKI$#ZB zqP&@ukxtH0x3NxHk)Cs~fwb5~0U($zHv`Q#pG(UQvRLwF#R~|jCrBZWy?gm5il-~P zAJ7Afd~2&Zi~ghhy9#L`s@kNK?|+#Hm76Bbf-LYq;;erw_Vcv@(N9yycXo7{B%@2% r&;&RzsoLb_q#Y*L!{lc8zjg}K1k77SE+1`_VtURSHaQfoXT<*-4th1# literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index cdda0d1..1471909 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ browser-use langchain-google-genai pyperclip gradio -langchain-ollama - +langchain-ollama \ No newline at end of file diff --git a/webui.py b/webui.py index eef1e3c..a1e81b4 100644 --- a/webui.py +++ b/webui.py @@ -4,62 +4,56 @@ # @Email : wenshaoguo1026@gmail.com # @Project : browser-use-webui # @FileName: webui.py -import pdb from dotenv import load_dotenv load_dotenv() import argparse - -import asyncio +import os import gradio as gr -import asyncio -import os -from pprint import pprint -from typing import List, Dict, Any - -from playwright.async_api import async_playwright +from browser_use.agent.service import Agent from browser_use.browser.browser import Browser, BrowserConfig from browser_use.browser.context import ( - BrowserContext, BrowserContextConfig, BrowserContextWindowSize, ) -from browser_use.agent.service import Agent +from playwright.async_api import async_playwright -from src.browser.custom_browser import CustomBrowser, BrowserConfig -from src.browser.custom_context import BrowserContext, BrowserContextConfig -from src.controller.custom_controller import CustomController from src.agent.custom_agent import CustomAgent from src.agent.custom_prompts import CustomSystemPrompt - +from src.browser.custom_browser import BrowserConfig, CustomBrowser +from src.browser.custom_context import BrowserContextConfig +from src.controller.custom_controller import CustomController from src.utils import utils + async def run_browser_agent( - 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 + 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, ): # Ensure the recording directory exists os.makedirs(save_recording_path, exist_ok=True) # Get the list of existing videos before the agent runs - existing_videos = set(glob.glob(os.path.join(save_recording_path, '*.[mM][pP]4')) + - glob.glob(os.path.join(save_recording_path, '*.[wW][eE][bB][mM]'))) + existing_videos = set( + glob.glob(os.path.join(save_recording_path, "*.[mM][pP]4")) + + glob.glob(os.path.join(save_recording_path, "*.[wW][eE][bB][mM]")) + ) # Run the agent llm = utils.get_llm_model( @@ -67,7 +61,7 @@ async def run_browser_agent( model_name=llm_model_name, temperature=llm_temperature, base_url=llm_base_url, - api_key=llm_api_key + api_key=llm_api_key, ) if agent_type == "org": final_result, errors, model_actions, model_thoughts = await run_org_agent( @@ -79,7 +73,7 @@ async def run_browser_agent( save_recording_path=save_recording_path, task=task, max_steps=max_steps, - use_vision=use_vision + use_vision=use_vision, ) elif agent_type == "custom": final_result, errors, model_actions, model_thoughts = await run_custom_agent( @@ -93,14 +87,16 @@ async def run_browser_agent( task=task, add_infos=add_infos, max_steps=max_steps, - use_vision=use_vision + use_vision=use_vision, ) else: raise ValueError(f"Invalid agent type: {agent_type}") # Get the list of videos after the agent runs - new_videos = set(glob.glob(os.path.join(save_recording_path, '*.[mM][pP]4')) + - glob.glob(os.path.join(save_recording_path, '*.[wW][eE][bB][mM]'))) + new_videos = set( + glob.glob(os.path.join(save_recording_path, "*.[mM][pP]4")) + + glob.glob(os.path.join(save_recording_path, "*.[wW][eE][bB][mM]")) + ) # Find the newly created video latest_video = None @@ -109,31 +105,34 @@ async def run_browser_agent( return final_result, errors, model_actions, model_thoughts, latest_video + async def run_org_agent( - llm, - headless, - disable_security, - window_w, - window_h, - save_recording_path, - task, - max_steps, - use_vision + llm, + headless, + disable_security, + window_w, + window_h, + save_recording_path, + task, + max_steps, + use_vision, ): browser = Browser( config=BrowserConfig( headless=headless, disable_security=disable_security, - extra_chromium_args=[f'--window-size={window_w},{window_h}'], + extra_chromium_args=[f"--window-size={window_w},{window_h}"], ) ) async with await browser.new_context( - config=BrowserContextConfig( - trace_path='./tmp/traces', - save_recording_path=save_recording_path if save_recording_path else None, - no_viewport=False, - browser_window_size=BrowserContextWindowSize(width=window_w, height=window_h), - ) + config=BrowserContextConfig( + trace_path="./tmp/traces", + save_recording_path=save_recording_path if save_recording_path else None, + no_viewport=False, + browser_window_size=BrowserContextWindowSize( + width=window_w, height=window_h + ), + ) ) as browser_context: agent = Agent( task=task, @@ -150,18 +149,19 @@ async def run_org_agent( await browser.close() return final_result, errors, model_actions, model_thoughts + async def run_custom_agent( - llm, - use_own_browser, - headless, - disable_security, - window_w, - window_h, - save_recording_path, - task, - add_infos, - max_steps, - use_vision + llm, + use_own_browser, + headless, + disable_security, + window_w, + window_h, + save_recording_path, + task, + add_infos, + max_steps, + use_vision, ): controller = CustomController() playwright = None @@ -177,14 +177,14 @@ async def run_custom_agent( no_viewport=False, headless=headless, # 保持浏览器窗口可见 user_agent=( - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' - '(KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36' + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36" ), java_script_enabled=True, bypass_csp=disable_security, ignore_https_errors=disable_security, record_video_dir=save_recording_path if save_recording_path else None, - record_video_size={'width': window_w, 'height': window_h} + record_video_size={"width": window_w, "height": window_h}, ) else: browser_context_ = None @@ -193,17 +193,21 @@ async def run_custom_agent( config=BrowserConfig( headless=headless, disable_security=disable_security, - extra_chromium_args=[f'--window-size={window_w},{window_h}'], + extra_chromium_args=[f"--window-size={window_w},{window_h}"], ) ) async with await browser.new_context( - config=BrowserContextConfig( - trace_path='./tmp/result_processing', - save_recording_path=save_recording_path if save_recording_path else None, - no_viewport=False, - browser_window_size=BrowserContextWindowSize(width=window_w, height=window_h), + config=BrowserContextConfig( + trace_path="./tmp/result_processing", + save_recording_path=save_recording_path + if save_recording_path + else None, + no_viewport=False, + browser_window_size=BrowserContextWindowSize( + width=window_w, height=window_h ), - context=browser_context_ + ), + context=browser_context_, ) as browser_context: agent = CustomAgent( task=task, @@ -212,7 +216,7 @@ async def run_custom_agent( llm=llm, browser_context=browser_context, controller=controller, - system_prompt_class=CustomSystemPrompt + system_prompt_class=CustomSystemPrompt, ) history = await agent.run(max_steps=max_steps) @@ -223,6 +227,7 @@ async def run_custom_agent( except Exception as e: import traceback + traceback.print_exc() final_result = "" errors = str(e) + "\n" + traceback.format_exc() @@ -240,10 +245,9 @@ async def run_custom_agent( return final_result, errors, model_actions, model_thoughts -import argparse -import gradio as gr -from gradio.themes import Base, Default, Soft, Monochrome, Glass, Origin, Citrus, Ocean -import os, glob +import glob + +from gradio.themes import Citrus, Default, Glass, Monochrome, Ocean, Origin, Soft # Define the theme map globally theme_map = { @@ -253,9 +257,10 @@ theme_map = { "Glass": Glass(), "Origin": Origin(), "Citrus": Citrus(), - "Ocean": Ocean() + "Ocean": Ocean(), } + def create_ui(theme_name="Ocean"): css = """ .gradio-container { @@ -283,25 +288,27 @@ def create_ui(theme_name="Ocean"): } } """ - - with gr.Blocks(title="Browser Use WebUI", theme=theme_map[theme_name], css=css, js=js) as demo: + + with gr.Blocks( + title="Browser Use WebUI", theme=theme_map[theme_name], css=css, js=js + ) as demo: with gr.Row(): gr.Markdown( """ # 🌐 Browser Use WebUI ### Control your browser with AI assistance """, - elem_classes=["header-text"] + elem_classes=["header-text"], ) - + with gr.Tabs() as tabs: - with gr.TabItem("🤖 Agent Settings", id=1): + with gr.TabItem("⚙️ Agent Settings", id=1): with gr.Group(): agent_type = gr.Radio( ["org", "custom"], label="Agent Type", value="custom", - info="Select the type of agent to use" + info="Select the type of agent to use", ) max_steps = gr.Slider( minimum=1, @@ -309,26 +316,33 @@ def create_ui(theme_name="Ocean"): value=100, step=1, label="Max Run Steps", - info="Maximum number of steps the agent will take" + info="Maximum number of steps the agent will take", ) use_vision = gr.Checkbox( label="Use Vision", value=True, - info="Enable visual processing capabilities" + info="Enable visual processing capabilities", ) with gr.TabItem("🔧 LLM Configuration", id=2): with gr.Group(): llm_provider = gr.Dropdown( - ["anthropic", "openai", "gemini", "azure_openai", "deepseek", "ollama"], + [ + "anthropic", + "openai", + "gemini", + "azure_openai", + "deepseek", + "ollama", + ], label="LLM Provider", - value="gemini", - info="Select your preferred language model provider" + value="openai", + info="Select your preferred language model provider", ) llm_model_name = gr.Textbox( label="Model Name", - value="gemini-2.0-flash-exp", - info="Specify the model to use" + value="gpt-4o", + info="Specify the model to use", ) llm_temperature = gr.Slider( minimum=0.0, @@ -336,17 +350,14 @@ def create_ui(theme_name="Ocean"): value=1.0, step=0.1, label="Temperature", - info="Controls randomness in model outputs" + info="Controls randomness in model outputs", ) with gr.Row(): llm_base_url = gr.Textbox( - label="Base URL", - info="API endpoint URL (if required)" + label="Base URL", info="API endpoint URL (if required)" ) llm_api_key = gr.Textbox( - label="API Key", - type="password", - info="Your API key" + label="API Key", type="password", info="Your API key" ) with gr.TabItem("🌐 Browser Settings", id=3): @@ -355,51 +366,51 @@ def create_ui(theme_name="Ocean"): use_own_browser = gr.Checkbox( label="Use Own Browser", value=False, - info="Use your existing browser instance" + info="Use your existing browser instance", ) headless = gr.Checkbox( label="Headless Mode", value=False, - info="Run browser without GUI" + info="Run browser without GUI", ) disable_security = gr.Checkbox( label="Disable Security", value=True, - info="Disable browser security features" + info="Disable browser security features", ) - + with gr.Row(): window_w = gr.Number( label="Window Width", - value=1920, - info="Browser window width" + value=1280, + info="Browser window width", ) window_h = gr.Number( label="Window Height", - value=1080, - info="Browser window height" + value=1100, + info="Browser window height", ) - + save_recording_path = gr.Textbox( label="Recording Path", placeholder="e.g. ./tmp/record_videos", value="./tmp/record_videos", - info="Path to save browser recordings" + info="Path to save browser recordings", ) - with gr.TabItem("📝 Task Settings", id=4): + with gr.TabItem("🤖 Run Agent", id=4): task = gr.Textbox( label="Task Description", lines=4, placeholder="Enter your task here...", value="go to google.com and type 'OpenAI' click search and give me the first url", - info="Describe what you want the agent to do" + info="Describe what you want the agent to do", ) add_infos = gr.Textbox( label="Additional Information", lines=3, placeholder="Add any helpful context or instructions...", - info="Optional hints to help the LLM complete the task" + info="Optional hints to help the LLM complete the task", ) with gr.Row(): @@ -414,54 +425,74 @@ def create_ui(theme_name="Ocean"): with gr.Row(): with gr.Column(): final_result_output = gr.Textbox( - label="Final Result", - lines=3, - show_label=True + label="Final Result", lines=3, show_label=True ) with gr.Column(): errors_output = gr.Textbox( - label="Errors", - lines=3, - show_label=True + label="Errors", lines=3, show_label=True ) with gr.Row(): with gr.Column(): model_actions_output = gr.Textbox( - label="Model Actions", - lines=3, - show_label=True + label="Model Actions", lines=3, show_label=True ) with gr.Column(): model_thoughts_output = gr.Textbox( - label="Model Thoughts", - lines=3, - show_label=True + label="Model Thoughts", lines=3, show_label=True ) # Run button click handler run_button.click( fn=run_browser_agent, 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 + 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, + ], + outputs=[ + final_result_output, + errors_output, + model_actions_output, + model_thoughts_output, + recording_display, ], - outputs=[final_result_output, errors_output, model_actions_output, model_thoughts_output, recording_display] ) return demo + def main(): parser = argparse.ArgumentParser(description="Gradio UI for Browser Agent") - parser.add_argument("--ip", type=str, default="127.0.0.1", help="IP address to bind to") + parser.add_argument( + "--ip", type=str, default="127.0.0.1", help="IP address to bind to" + ) parser.add_argument("--port", type=int, default=7788, help="Port to listen on") - parser.add_argument("--theme", type=str, default="Ocean", choices=theme_map.keys(), help="Theme to use for the UI") + parser.add_argument( + "--theme", + type=str, + default="Ocean", + choices=theme_map.keys(), + help="Theme to use for the UI", + ) parser.add_argument("--dark-mode", action="store_true", help="Enable dark mode") args = parser.parse_args() demo = create_ui(theme_name=args.theme) demo.launch(server_name=args.ip, server_port=args.port) -if __name__ == '__main__': + +if __name__ == "__main__": main()