From 00148028d6634e6f3db53653d0e024f018855cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=B3=BB=E6=98=8E?= Date: Thu, 19 Sep 2024 11:02:39 +0800 Subject: [PATCH] Dev modelhost v2 (#137) * update: test * update: test * test * update: test * update: test * test * update: test * update: test * update: test * update: test * update: test * test * test * test * test * test * test * test * test * test * fix * test * test * frontend * update: test * init * update: list-files * init * fix * update: list-files * update: list-files * fix * update: list-files * fix * Dev modelhost yaochi (#109) * remove florence2 caption node (#98) * fix bug of category name (#100) * Add ultimate-sd-upscale (#102) * Add ultimate-sd-upscale * refine * refine * ultimate_sd_upscale * refine * remove model that cannot be used in commercial * Update README.md * Update README.md --------- Co-authored-by: Yao Chi * add sr deprecated message (#103) * formatting code (#106) * formatting code * refine * Refactor Bizyair and Code Formatting Improvements (#104) * Refactor Bizyair and Code Formatting Improvements * refine * Delete redundant code * Delete redundant code * fix UNIQUE_ID * refine * refine * refine * skip Super Resolution test * Add lora test server (#105) * refine * Delete src/bizyair/commands/servers/server_base.py --------- Co-authored-by: Yao Chi * reformat * news to new url --------- Co-authored-by: FengWen <109639975+ccssu@users.noreply.github.com> * add Single file upload * fix: add get_api_key * fix: dependencies check and invalid api_key errno * fix: dependencies check and invalid api_key errno * fix: print message * refine the text content * reformat * fix * refine content * update: reduce allow types * update: reduce allow types * update: reduce allow types * update: merge requirements_adv.txt to requirements.txt * fix * fix * fix * fix * reformat * update: change all server_address to bizyair-api.siliconflow.cn * update ci ComfyUI version * reformat and updage * fix * fix * update: upload limit * update: upload limit * update: upload limit * update: upload limit * fix * update: upload limit * change to wait for bizyair logo * fix * update: adjust structure * fix * refine the queue btn CI * use comfy queue btn class * use js to wait queue finished * refine * write ini file and reformat * fix: oss progressbar * fix * update: clear upload cache * fix * fix * fix * Fix Model Find (#112) Co-authored-by: Yao Chi * remformat * fix * fix: model repeat delete * Dev modelhost async upload file (#113) * refine * refine get loop * chagne to async upload * fix: path_manager.py * fix: remove useless dependency * fix: service_address * fix * flux dev fp8 (#107) * flux dev fp8 * refine --------- Co-authored-by: Yao Chi * fix: service_address * add docs for lora model host (#115) * refine * fix the url route * update readme * update: replace to websocket * update: upload by ws * update: upload by ws * temp * fix * fix * fix: upload by ws * fix: upload by ws * fix: upload by ws * fix: upload by ws * fix * update: send pong when receive ping * update: send pong when receive ping * update: send pong when receive ping * fix: execution.py * fix * update: add valid_user_info * update: add valid_user_info * update: send synced status * update: add valid_user_info * fix * fix * fix * fix * fix * fix: modelhost.py * update: modelhost.py * fix * fix: modelhost.py * fix * fix * adjust the timeout setting and refine the try catch block * fix * add docs --------- Co-authored-by: dantegarden <761043617@qq.com> Co-authored-by: root Co-authored-by: Yao Chi Co-authored-by: FengWen <109639975+ccssu@users.noreply.github.com> Co-authored-by: Li Jing Co-authored-by: Your Name --- README.md | 1 + docs/docs/bznews.json | 3 +- docs/docs/model-host/imgs/model-ready.jpg | Bin 68851 -> 0 bytes docs/docs/model-host/imgs/modelbtn.jpg | Bin 19372 -> 0 bytes docs/docs/model-host/imgs/modelsmenu.png | Bin 0 -> 25011 bytes docs/docs/model-host/imgs/remotefolders.jpg | Bin 67429 -> 0 bytes docs/docs/model-host/imgs/remotefolders.png | Bin 0 -> 41224 bytes docs/docs/model-host/imgs/uploadfiles.jpg | Bin 84338 -> 0 bytes docs/docs/model-host/imgs/uploadfiles.png | Bin 0 -> 24934 bytes docs/docs/model-host/introduce.md | 23 +- docs/docs/news.json | 3 +- js/apis.js | 51 +- js/dialog/apiKey.js | 129 ++-- js/dialog/modelDialog.js | 106 ---- js/dialog/modelList.js | 47 +- js/dialog/uploadFile.js | 43 +- js/dialog/uploadWithInputPage.js | 266 +++++++++ js/itemButton/btnApiKey.js | 5 +- js/itemButton/btnExample.js | 81 +-- js/itemButton/btnModel.js | 23 +- js/itemButton/btnNews.js | 80 +-- js/menus.js | 19 +- js/subassembly/btnMenuFn.js | 79 +++ js/subassembly/dialog.js | 149 +++++ js/subassembly/socket.js | 107 ++++ js/subassembly/styleDialog.js | 140 +++++ js/subassembly/styleUploadFile.js | 16 +- js/subassembly/subscribers.js | 23 + js/subassembly/toast.js | 71 +++ requirements.txt | 3 +- showcase.py | 6 +- src/bizy_server/__init__.py | 8 + src/bizy_server/errno.py | 41 +- src/bizy_server/execution.py | 54 ++ src/bizy_server/modelhost.py | 613 +++++++++++++------- src/bizy_server/oss.py | 31 + 36 files changed, 1578 insertions(+), 643 deletions(-) delete mode 100644 docs/docs/model-host/imgs/model-ready.jpg delete mode 100644 docs/docs/model-host/imgs/modelbtn.jpg create mode 100644 docs/docs/model-host/imgs/modelsmenu.png delete mode 100644 docs/docs/model-host/imgs/remotefolders.jpg create mode 100644 docs/docs/model-host/imgs/remotefolders.png delete mode 100644 docs/docs/model-host/imgs/uploadfiles.jpg create mode 100644 docs/docs/model-host/imgs/uploadfiles.png delete mode 100644 js/dialog/modelDialog.js create mode 100644 js/dialog/uploadWithInputPage.js create mode 100644 js/subassembly/btnMenuFn.js create mode 100644 js/subassembly/dialog.js create mode 100644 js/subassembly/socket.js create mode 100644 js/subassembly/styleDialog.js create mode 100644 js/subassembly/subscribers.js create mode 100644 js/subassembly/toast.js create mode 100644 src/bizy_server/execution.py diff --git a/README.md b/README.md index ba57e723..1d91c9d9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # BizyAir +- [2024/09/19] 🌩️ BizyAir has updated the model upload feature, allowing the upload process to display a progress bar, and the size of the models uploaded is no longer restricted by ComfyUI. (https://siliconflow.github.io/BizyAir/model-host/introduce.html) - [2024/09/06] 🌩️ BizyAir supports InstantID for SDXL now. [SDXL InstantID workflow](./examples/bizyair_sdxl_InstantID_basic.json) - [2024/09/05] 🌩️ BizyAir supports users in running custom LoRA models, including SDXL and Flux LoRA. [How to upload and run custom model](https://siliconflow.github.io/BizyAir/model-host/introduce.html) - [2024/08/23] 🌩️ BizyAir now support ultimateSDupscale nodes [upscale workflow](./examples/bizyair_ultimate_sd_upscale.json) diff --git a/docs/docs/bznews.json b/docs/docs/bznews.json index e5b9c72a..9ae9b0b2 100644 --- a/docs/docs/bznews.json +++ b/docs/docs/bznews.json @@ -1,3 +1,4 @@ { - "09/06 发布 v0.2.0,支持试用自定义 LoRA,欢迎升级使用": "https://siliconflow.github.io/BizyAir/model-host/introduce.html" + "09/19 更新模型上传功能,显示上传进度条,上传模型大小不受 ComfyUI 限制": "https://siliconflow.github.io/BizyAir/model-host/introduce.html", + "09/06 发布 v0.2.0,支持使用自定义 LoRA,欢迎升级使用": "https://siliconflow.github.io/BizyAir/model-host/introduce.html" } diff --git a/docs/docs/model-host/imgs/model-ready.jpg b/docs/docs/model-host/imgs/model-ready.jpg deleted file mode 100644 index 8c3fc2ef554043494d500ee82d12ab5bb067b6ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68851 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

{FQE`wyKCN70NV*8t%HyqPP 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≦7E{E;6G7y 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 R9PeOU60CB=N{Idb{{YlLYaRdq diff --git a/docs/docs/model-host/imgs/modelsmenu.png b/docs/docs/model-host/imgs/modelsmenu.png new file mode 100644 index 0000000000000000000000000000000000000000..4b2bcc0d6c0d5312aa56c8fd02ded11d3abcae57 GIT binary patch literal 25011 zcmV)bK&iipP)PyA07*naRCt{1eR-T5Rki>3+*{Q>Ju{h1_MIeTA%uhg2_Xra>?(vEWsyZh5Jl7{ zZn%L@)Tcg0P!tth0GB7S$tL@bBoGK8B!MJk-zGDerKh{A>fUpHe^hr*@6$7rK*Epn z;S*}AZ=E`KJ^Lxzd+)uG13?f(A`uZmc?g2Qb=~lqh%${80D$|9r?5@8czoXn5Q#*x zcf$5WRH02tNG6j+R8vz^a6gy4T(V075&6Dfvdtci2bp1XT{m}Z3&8U{BI@-}=TcP- zNflPn9*w8Mip|9XAVft(3;{st7lu1g0Z1j1L==sc80uoO%h6mV+3yw}1`q_c*LboZ zBH=JrUa`LKi%2vYEqJzIBP0?DrBumPT})C1s@~1>vsZZXPW!GljtfAj3s1oi6|ZVl z{IbgAR^RwN!4m=zRX8H6c}wL#dxYwJgC`5QV_Q7VeH{5-Q&s%n`vy;CwA~^Xf>5;t z>~4MEMR>AM{cgn4oDscD#rlJlcGWlLSm7XYmMD*|p-nybth{#N3OgDpQiZSsD5JwM{jU#glvRg}W~U3Q*lndKRiV zKzFA$3Q*eIV+fV-xEB!P%P<}yIBsbwE(o%1rK&hZIzVo(Riu>5y>@8Q~+ZP5rsot zKtS)v$W<@iPk0J&mcvuwCu59KN~sbi_ikU&Yo+Zy!9!A(*Y9wu4L%#6$d zQh|(ef1=`U-J|i8JBOVD|64hibwQhM{mkzs9?$at6gUZc(FPg!c(*>uJ#L70A|7NM ziRG45l|diY+G>Wn8nWN7ZBT`KyxTSaj4{=q+TFU}i#DP5lgorE;K?rFDu-H%A`yzA z$^&F~+tlwGp5k-3&{EtZDDa)+{Gv+6IP@G(wU2lER5fi1XsF(XUM0f!8J?0dbx*NX zXx}4L?^it4yrLY!ZW~lj*zP)ph38C3dt*5~RVsy5lGF>>t+sR3c)I-kAry~!<%ui! zwJL*2IoK)wlAVZWXF=L}zVjR>yW2qREJs&Kn`e)3_R$sezQ-|$no|8d;oob-k&qQkT0w9tH1wO zz5*0Ogg_)z9Q9Ecli3Y8y`zFi!#dj3D~>@Nk|NKW=S597Hq)KehC(y0sbqqzrVRj2 zCW$D@l@iL{&93ckZ3>1lSCH)TSXMpM0HPdw7s%cd+f+4-p;f&Us;nF!WCO32RN$!E z&>0UfpbGxAUf{qjzP~565l3;f@GL4b)Okxc9BNy|0b(Pls6YMxu&RYD$XK0Z9f$UT zKI!zm<3I{b%I`lkGIQdHxdZFQxKvl5!SzlSD0uKal%}(b>^<+x?uMr_+MNZ}pnbo# zsRpW_O;(*t*F0yP4u}GA<$r~Vl%_DmXWc#H}LnJv)n+AMf3f`6ADlZZl-Y|Laf_K`tc}qY^QN8!M^oDIH zi8%h?3BzW)!BEExQ0#{6r$94h0(Z9t$~h8xK~({Uog2}x`*={_Lp<*e>~5~q-2qQI zoLLBSWU|)<+pZu}y%+d*pEku&wj=;|^*#NRDoh*Nt(N-EI``gvJVd0#)j>l|Fl^GW zLtKBblF)kDOP^e?6c=`3>IT-2C0k220&%j$^%PUdNBSpf@ZhbGbtbn*s z$LrGiB(srK(aU$yTelfun z{>-OVoOyj%Nhwm=^s z7%~8-j~_Dqz#(_tvbud^T${Rb?39%e6$hEznIj@$m$-(Y1xDaOGXQOCb6ry+QczE@ z!rU;kXe&AeGGU&R;%FKh8LK@&NC@I6xP=T)ROTaLJq8g08CHRiB>^aOttxXsA#q-Lr zi~6-wAT1Rr0~nA6od4rE9o+SFGW@mrE(<;$A3pnb{f^c^L_>D7pwCD$z()kB{6+eH8u|5|(}5{XD8 zV%-ROTkX)vMc2EIm5?;kUxz@U!M5Nr0%|d7E>6eZfBg9(+wN$SVSZ_yBNJe*Qhv=w zaoW>)0O`BhvGwE)RUBm%z=<2DMZ=-QXl?6r56aB2cH^u{s~X|EbW(Ax3hJC-pt4GX zjPgumE2}`;7};8weoC@wTp2yA4X^OrL4>tEhX9Mz_tq~KT~076|v7HD}{o>BHZ5ruSbyLe2<0<(_` z0YnHtHbwyQs*Z*qAq(F~;hLv3Ni34L0;%-I%#NPbos$pg_sRbnoDSy7&S4mJM}Weu zKU=-x#b(DJs-W{0cII87<-@`%Kv+@EPD$&0NH41FkYv@t5_E>wtU#Q$ax(!4_YsAG z$0geXAX~cc<*!BZ?q`-n&UC>Rw=WRU(yTFOrpj4j3cxAmZ+(~4ICr1gOu7<~=OoqX z*@cVo`(dvsjcZ;`Y1EfSsFOdrZ4RO+aI<<_aO(|Q*1pvO;4@e5H*PM#_~ysohAoY! zG6*cIz`89z`NJI40S5oPsq^J0wkR7D5vJfXUz|DqV8Gz(7c7CT%Rh8fw`1MS+bKBX z+-c*dK!trI5U_FuAo%CsmH?POW6DQQc9}q7m^(oT@2tSRe_RXXimwjwyD-U0q>aRYS?n1y+HmBoixf zAg<;jjkc?$0R=)9U?NAEbnqvuz&ahG0;IQGIKq)ICtm;%lc=nQn!&>)3gRfyOjeW^ zYTFQ(*sX@0^j&1k;z1~4lLZ)Lh0NzZf9ZbvkGl1?|2t^r0gpWQlY9TV_BX${Ra8Kt zAey9KDuv}L=HKFXDZ`T+|;6rhlnNFX8#B+4*+ zA|klNj$~}DS-WmVR|p>yWNi{zwgjZ}Skp~};Z2-6{^`fPyYF~daTM8#3ly-^ar!wQ z*?YeM7k_2%Km2I(vX{0x!9bO%cOV)3Ocs<9vP?$9RVE_P5M~vmmq*&qou(mcs~6a? zZ%FqtL~296OXc@6wd_|2OT9QPN?5%1sH}c#vvphdn?BXCc=Od};)p1Z zV>^yLWm3a1K*6cUprN&EsI{vPA)n8_Q6&)rqJTk{sNx_x7MEW;{L7zO?05shJ1}5n z%XA>eqk|IMo++?y>&y@BjYhx)1V^1-xA5sWQ=<|EGFAZ^un_WvSdq?fot+R0$jTgi z68>=GJB~L*aUBC7v+q=Q+!UPr$>{*5jK%%;rXVgvDZo1I<7d<#K40JU=afc6K`$mw z9~v9v8y(jfp-$Pl#J0%FH@72bP!i3xo~8dyk97$HLf5p0c@`YXI%(~vc)>6w22bP> zBStKFW-Cbpgw^)M^kN&&JbRw9_~&2VBELaNH0|9% zhG&P1M0wWTvG0uWj-y^*^tQ``$Y>(6PRH~k2H5Bh02>ZFRv7v-_?<{Wy^wLdxYzB@L5_S4@5oP|#>|LxE1XRi44LoZ!>_ut<7 z^^G?Z$F!*>L<%E@$cReLoHMPq(S3W_>Midih+QET3J^^k`Js6>py!@^iKLeD-F<;jF!z{g>(;H?^mYfqNozkLww!oB20*MT zUIXajNdQdLO^sBhAv3nH+OtmF2?o5qeA8QRCqMc13Ag+>@zx7*#ZjAagQZ76`PONl zJber~!JDtIj>s@=Ype>Kx?sP9N3DEoRmT>FtKx}~}Go+0|XAdC68Dn6Y9;8`g zmgej#t*XBzuUUB}vSa#;X>Yu;MsbWG0IAHHXA##7WE(@4wv26xq%iv@JxzHwj3Vnu zDJbMX)ol23!f7%0;v{bS-92Ptha9ud)CsuhN9*5NzFl$DI<0l1TIQcQDT5KJg2xCD z6+i`xszU?@Iw92M?-C&e z7YG-Z;{=Vu8g7cUC*+evbkHEmGj8i~CryEq9+-jS5p4`iQ$i#rB=n@vD*KIBf=my?$eHGKe&N<-d;|u{XYa_ET;! z7!rjzBrwq?uQq!4hi8z^=YQI;6aUneQ!prViiUGyfNl8t7dBd_LpUI=0U*i)gg7dI zC{GX&5kyh87i5Go0*5FPErd|=B8~zIWYR)35Lyn2YuNEm`q<>jGx7QhKCun`TOgCc z>>!lzLJa_bI8C*oJGq^{m&4voD%DXc87)6xXJIdQ(u)Fif5G z1+4zhfSZ4{PIOXq;tSuHJ7yYE>#+FcBr63@zzNct8Hm`)jH&`VM8aWwsYHrnN2`R~ z&CA51?Cis*f8yfPmn>O&>=9pZgZ{*|Bn|)}bJMSWcjIqcFTdjANB;A_ciq0?ch~<- ztX7mKx)F)h-hco2Eb50%tMI9_z9UitfR#Dp?Bg%};*kh&!n~)*M4$v@MJ$nq5F`>V zP=OI*QJ!_;*ZlC*=B>Es{BIBw$dZ(MW0Yqp2%WgvMgb+-gDBz{08vR1 zQ7QsaVH7cw0>H`#SSbaG5W5yib!kFK+bz>-pj6Y}+-oM^sP^-}p#Su<2}@sHt)KyG z9FLv$`R5!mc_M!Powpo{iU03%)jYZGnWnWssJT)plTi?D!l=61r}Ir-wI2? zamOEX;wKtbzkw}l+Z}(f5>nCBaUVI<4FwJh8^?|&k8+49W2`}t!upjh+Ayo+^H0Ba z(wPT9U~OP^aM*_?&-yTo!Ggyhxa^8q`^+os-0LsPep5)-qG=U=_O&+^$Gqr5RgCbr~~JG$U!Hbf3`K+V84I; zhv4GX*W(8sQjY-txfA#PGr$Q3rmYiz z*f??cE!P5fg=Lk50zCZj*UMEfSesfABDnFEfS^pINlC=ZIrt$Y-(uE3R|AVbFnf00%;T=-;wV z94ZNMbcLGYAI>#GkZA@2+U8xdsmt|fATH48^l3sMf85bQo_hbOD zZos<3VvrI+UYu3hD9aadgli#3s@VunroTYZcH7CX{2PV1TVoUVHuN%im1=>ZThs*Au&0Qyali z91$D)^)G+-%4^eq@Z-x~fBAryo_#}vh@7l1LBK)*YiI`}CqT!T;|Agg{Ewb~I2?c> z$`j>5h;>4g9|{~Qs3Sx`z9`Q+39I5v2}=k7rc^i+qLLs(aJ7w+t+ix)L4bBvR$0qg zcQX--5UfZbVnT%>Yia_NWUP6aJ6+czL1;h{2}HV{5+-G8mMz?T)j6xJ+kE1AhlDmd z3zK01X*-RO&ngjUC>N((FyyrtSEQN&I01KnVu5ZI_^c8p(qx?ukR==CU=WEiZmSZ< z@rM8~ktQZ~{6T;c9TycqTu@-04yHhf)~2814ZxYq2l?(9EE&pC1c9E6N&$0n>1h z`;I<3EffSw9I}pLmjJ*O3uP0lS3rf^e8W1dceWBwGQ(OyC#1U=l z$L>A)@@w|nu?he4tyh@&zrK6$@NxL{waweNZ@=Q}vj>a?9Kd438~DXfo(1S9zdG!l zRTw`7s0BK4!%vr7eDU=9F+d!T--$;ad3o>sMqc)nk?*{T@zVi?_OzgM-Tc38(k zYtbny?S~#eW&YU?VnBd57vK*!Kc{TJsnhql@X8732UsLGZP+SiSVG@pLa+`04d)FT@vP8fao39joM^{+p?)AdAl0h6(0S*P}&h0U9=V@Lbc zNeym2n;_wE4Pcj&o! z8igfK;ZHX`9W{Fa#F3^?Tsmv|JirB#xbOc`&p!J4C6~>fI1}Hy>NRg$l$AOD)agf` zhF@Ks+`Kw21)@jKLCDOVcko+Fy(MofdHk81@4J2NuWz_jC`F_|BE&>?-kd`}@u~T* zzq#n`rB=e+^CFvTm?Y}?h(_?Sb5FhF*87x=#_ILx@ya)dD6sP2bX<7hh38&y07bw7 zo_l)x55Dyak-)0NC7-=u{>jrKF>Gq~Siy)jNQ(BZy6VeE9zDTzkxJm!-#+-zJA{0nWAcn;YDYQ9us;*?+qHDtev$e1RdqCVO4=|aoDX`K(E!ap9u|n2b z;!ux)O&`p#)(a%zTbLB08lU;{VgK`;2g&re)Zkh8X!of_>(s|Cns?A4a9wx_-2cz` zGtWHz^=nUb6@K`&mjOQSvU&R-fE$0XX2aS|qxY%1@=G)ScE{EsLk1oFVMjX<#@kEr z`=7l`hA6H6G60+7?N48q>SMWU%U&P}VFbFx(^~;K^{iu${0LTV#PT(54)r&Zpv-tm zj#Ut%n0@YY24$RixvQ&uR#q+)%23q@KIS}=R}&%E71VDaF1zX@=!dnhV#y0l+SIX1 z>c*CHKRd(e4*>Ad`VRj7+YM$%197aIS1UvY;?R>C=bS>*jsj`{;P*GaOdQKZBmn|# z8Xo$GdGdBVa!1PxPpnS+jRHJ%&zficwFwM3IO?Obf=JU5ADs#pFnIW`7XZiV)FDSk zp@Ae8J-dG0G7AR!WAXt*)7FnLXovuCm(|%|(PP23*YNx!%eCpJdYB}fi6P0etLC^< z(N$?Sz2S-~P>2c@_Pl!p)Zp-s?t9q$@rR#0_OO#jA92#eA){+otzWij0|rdSoKr^3 zJ8jhP=~%Z8Z*6?}(l6~ZU=r58gIAvce2kun*&iNZqFc~_@w3sIz}i*709<+9^!hPa z`Wg_!F&~3pv(ag6K@8&$#@5YPw;J{1@a1bKn8+5$HMQHleDTx`L&~QvepRnQ*nhy6q;?9JpKj znvGuV>QgQ{Xx^y`F5Z4)(@(#1XVY>x7LxO(4EteLfpuGF9K0`FJom`b2mkRR1gCs# ze;aLK<)w*Ko;a;RWUGm6n|sU*Xmqw0YNfdo4F#zSuD$-;LF2LDnfRZ7{k)qTN4B1I z{LD|!ntlRIC*E8D)Z*kzY9>w_vT*5kG~(zp_Onih=){~40}-s>uu4Ny-}FTxW-vr4 z6TSKR+m4_2RX}~_3m0DcUuT<0t8rUr9XaWSTduj_lk;!;!<{$Xa4T7zmxL}V^haj* zdt0+-Je+XiEbFvcx9yzsPY1AS#U=hS^SsLs5QXQS*_7~c#0i7HcI_3uyZz7; z_BrGHX-4qOGbL}0>j3z$wR^>@X)apFTb>=-zdyJVYIJW0+aH51h(CGww`p(A^Xok zb0?mE$@hVeoLfI)|6pxvQ|(}=x`gj+8#4{bD5lLFnsnD4aMaMw2$pSl>hKesmUcY+ z#KyH-Fm_)Ye*A%fZna2;y|ZGw>s@#MAz(2HgCuR(`@q;|u0D3o(RlPU(b zdb{h*Hnwc>+9k`jMu)U~|E7~*0fWc>(fa!HTSR$hUN#a@9DF$4`|r&YrYjH-qyjJ-tUz z=;{ZMm+{&a5mfM0_o!W*b>1vw3sV@p7-_uguBID)_P~#Red4L-kAwmexZxM~kg5@d zb#LRxKfj4g&E!QBuDtrlQ4<^NvmH=C65syzZy@RyH(aXvdRZUh{_$31r}RZ?4yFc793q!ack~Q z{p9+~&pLPf_r7=MIdtYhbKQIXw4=VhU+qvVe+3|sUNGn4{%HVE48>6u)cpE}-_JgL z#(@V<`^=S}oIZVD68z~;e|zb<*8nKxLZC|VyfkMO5Jz8KwD|bN*Z=?^07*naRO;Y^ zCq(-NsZQ^N6A#$B5kV>xo9y|QoB@q<&i>Z+t!?bIJ^b%KA93_Bb6wk67oL>#aoVYu z8Q%;aTYJ;ZH~PMBBz4#^Gh0*5=U((_FxB^0fBxeg=bV4qD_?sqk%;@gZ*2e$0Dy!f zEex`F0VkE{o>Xd9Mx|1zPFC#O#Nszzy8Me9trG_vVQ7geTOaX;SQU7FlA(-AF((Ra z50;X~dWlZ>0sip&zkc`oUp?ld`!9OoZ6Di{smZAz?$P%B4puuneB(RcB-;?Ht^M|O zpFj5GS&u*V=-%Vb-GBC&g^QjyuI(_vs0p%oF#~TxA&PLrAJz7YLMbk?B;Olof-Vml>)-fZ``Rr8G;Doa#EL!+_h8QSuK-NZE zuD8u+zHg4NN!)4P!BSitE+`0bY6%+SFUOZT^|lL4d~i1MsWc zKFcaSJfkOrYCY?*5F=OH5P-2|o#*(29DktdZH2yKb4sOIklf7baIjcOQTYP~61eTw z6*@K4;W_|hBWvDlbAm>*L;iW|)-$dc2H@5kR-tXUviiqAeC)9KW3RgU(qY4#+JOMz zL`jGt*s&3gH$+6@+tJ`+?VD|m8Pd@N0QLQAKn%vRSKy>ZE75Phg5eV}edZ)V0D{$> z{f7b?U-;?;WC23777$R-Z~ICN-XA~v&E;(^Sp4F)2ku)!Jc2T7iJ4X3!VpAv2DKZ$ z^6evrPQsRV0ATKXKsnDo)Oq)xpFeQwVN)m9E`4QV#0~>TAudxU&?uxqlqFBsbqt&g z5I+C)a{=YoAX1b|h`FkwH$hNg}`{%|lJzULL^V%qfD{)1`<46NU{ zal1__n7R#XcC>EylAaIEfk|2~F(9+@&D?n}DxC0pAIGJ@t68hD4>_}=81JKguIYBfUiCy>of1mQngJ#Yf(tj|H zIDY8C6OXy}%Zsz7$kKI>svb!ZVHKQq-VsBm;)VP1kH7s3z!~SxKkOtdU%t}yhb@1p zb@_s3jRuEngmrc>yQBePQQnDX9U0nbr%W1@_WWwhKW)mgXI>>@5LPZr{PKJEJN^)e zqfCts20Jv!L=9z9pb9shVDp=}|8LJdR`34k`m<)8fCUR`*Dr3bsdGRG0<^V+-d?m@ zbZh{L`{3Qre17T?$DcCufCGVcYW*3lP(Zbh*6psJsF%1p%0RHjUyAC<<@Gyf!1fB;HQmhSgJRUcu)>#3r-;gSp44|FRyxYGc!m6gG4;l0l>3yZB?fG zX&;QGPGb`BPV0F=kdmc~)-0L6Zm-GxCruyT8gELb;>wkdcq&Odnp!py*FZ>A(9s%j zDzfgK=DHzv^u+$nO>Hl~^m^m5LuVg0t+PG(^b^kqf&JkRZ#w4W1IA7sT3=s(^l?WF zi8VcZ*GrDI5SJv)eTBN|V})x=ohC?j#O2`!9~(P%?7X>$&D{5}1<$rbOt8HY?(RaF<~qmGNB5j3ztes=w)5F+`>M&IniDESLny^asM41 zO)IryV^(+84(~knbil*!fAQ#1Cmw(aKokSU-=sP>a3c3*!53bXJ zjznu`A3Yy60MOZ%b3iMY$hNc3K4aAWX{Y+%|9=0ZgNC|Zf4?QRW=+$oH=EH048nk+ z3XJXRV3L}ExkpV!3=lM}#;Vr>WnBQOHkkEc7le7o;;sjqMWsRxlsl$2lYL}Ep5Mb` zlXB{$WpCzOhc1mSr64Nt{VcN{-V^3}a)IQVaL9k@eib+#P z15oUGt%&x+O+Wo>a!Y>zXMXCi!Gi}yYWa&_JmL98Td%(IPqhv1wcj~6HVP`%DW)Nv zW7juf2v9zqeie+UWuXw&z8ef*%kP}iv1;~og$S|>RonhNC`MI05~nMaJGY#3gc&e5?0 zjz7#c#qPeP^R&;^edepvuDL-7M{()UUqs&g?_SA9eJR8`gPV z(!i#Vp?F6yaDc0093?|@@?xothH@zZSC>4fPIJjyp9MUlrkNP!-L2YWo?GpsIkIp5@o5Q zGugr-o*#eXjipPzzK96mbk?wNQj-%Y(2p zx88HfS3WXouR(3?E%A7~ZO4wbmbOmYdGoLSMnOLSr=NZ7wymu}N-ui(wS$fs9;{FEUp|Kpc)trBY(z@Tvu zK6llJV3E#0wSdLr_pMW*R33x@NOH(gnJ7WVrJqf^g3yYud}f^!^nZOJ_Wv*dz~Grj z*AvG8KJK`2A+TR0Dqe+w3B*xYpcdzS;#?h&3WJpd zK?47{=eaL_?Klwr@XKY|Q5Sq}pa1&G$>0C>KVZ_51s9BiL6in+6N63r z2|!A5821zd34kfo8OARE|Fo0aVcV!s9sVJbde_i|@W| z+nCWq4?6;zS0Uc!4Wvl=NOZwt@uN?#KkMT&Zn^bI7&ty0Gf11p z>?~O2D6UD@8DKXouN#Vu;k;-I9W*M__@Bp@9dhifk(2P*&(FQ#hQ~Lpz{rWX>WcIJ z{owlJP8u_AA8dFPI%p&tUGg%fp8zl(xNC{>`;{w8Uk!Af+M*yjV+CRBq_h=?&aOQs z2651@h(ip;Addb0wtHsJ{ZeCp{Nwf~$VMR%SSYD|D{_X$%{`a>Zf8ybFwOH{+Crs?|fB)xdCxxi++jx@Jbd`jH(y^B1j+5K+uJ*K_}p%->9SV$wYBlqL_L6nZ6X^N<%4V(^+OOqgCk;-ZOM87opy`1?MzBUG&I0WfK3Eudvp6H z&-0Rgo8v19ynFAx`-tNY6sC@NbHa-+UA}ba==x)iJ8JQQRR4T|4_jM;-d~b5HbdjI^|EX>0O2?KYPK zvNd@bsonLlh+zDPRY- zF4K`%_+bGeCS}H@?-D-1IJe?Et6JN2dN~82uPrD>L-rX zL0y+kQSjG)-fbcih-C7-TC#e|)Cr;dA}Tq4-xvr4Ca@qo(^{K;2moNthtgi+^nV7; zoret=j*YKj`LhX-Y&{agCgbQs&wS-c%sd8Py>1>XS~s9^G$xO7onSBmr~um3gG2{_ z59RmQ98(4epiQF+?KuX=p3(>gTJe)_uZXs~A z$E{FN+;#ioWMj$#3}qT`x$&NfV`j~oU4QNOKH&ve{kH$rFML;}8t=a2sS!i=nzH}M zb1wNvXQHj8V{=<_N5GxG``s-UTyWmN5d-F*eWc}J(d#cf{MdZ}H#cwF(b42{Ga;Vl zglvgONRe_oP)~^D^QB7HAPD@{WJJWa$;z?g$A0MegIIaU*2$LWlmA}0e*OB^qywn9 zyhDfy0Lte7gT)iu(cHc|Fs9RP(9&kDef71r5#t&L4Q^=b+@AE??z->xW9QH5H=yQ_ z<3_XH{>F;8o_*nIW$WXe!KTe?qqWi3-gqGu_^Vg195#H&E3dto44M;Ln_hb5@$r*K z&N*frE3UzUUzR@~yzh+}tY79X3I|yWJonWA{F(ww>Y26({ z>;w$}tkWK*9Ea^8E=(;!i%v~DWavd#PX@606x3+8>DPqzLwf!2qG<~Tr|PBP~3Gcl3?yfv2h(n zO$02qyxnC03#-GeS%&*>dCTz!WjT-~-#Q)a#I4iL%FBA!Ni;YrLZqy+F~=VUh>31x z@)gHG);etyr;J^@W^<780T4&DsfR>Kob*SYLw~O*unS^QNfT+_cg}!MeQE!D{=D&x zSKoG|u6bh+@kT%#6WInR*Bc@P6WNNIj`5R5uX$&a#OqX;k*Yis**a$8U}Nm&b*8;dxAvGT0bA>j!{f#^D`%ay>Wa%=KBJyjkl|T}K-mV+BTem$F ztteaTJuJ6DCcK#)MgWSVnYwV&btsC?{bX~Mz+_k@h(H__GVHWM`yqE15#p#a4I~az zib!c23-RY^>SS1XVS-S)nm%AD1!Y(TfYzoCVC%Mv@&G1wwba7YKwOZuZYu!g_ah>4 z;!I2?rX(6Bkg*PH4=UJ*WBO~U>2n%b4v=A$6cR|3SOY5a(NSY{l8BV4DHzYLj)%|c z6sPOLbiu}r8I(E1KG~{hG5_0XI~QO9Osxp1;6?=n`fWW|MW{o zW%T>(t8!Q{g&E@jfB>c$xBPVVnwPEXmrhD$WkjVwe&G+|q-(<+zwYBSCXG#}YNv5C8DS5)mfIP|DU5X^_gp|+^57IW;7sE)F zLhE!0*{~F_h=hi0cH&oEI|GD=?@GM!>M9bQqi%9oPbe=6oS4aq@&U`r3_ebZLKzS#4&(Gc>uJH zGC`CVmh=Wxcy=gJOe{LdWB@2}6xWKVET^$fXJk<4(Z}rb$_uSc8{%YRN}L|a`{A0g zPRE(&O*I-5C*Yxb<8QvYUIn$pF|tl2Ws8{8cC?V@?3u_+LB=u3LKe`4v)}bBLsVCl zrtGt#0G+XE1fXnYr4nLR9+0k=N*sv}=={W2#*fum}JnBGw9+tPPB$eAT1}Z4VFP>{B^a@Y5!ibOjI=CS@Qbz>$TP)@5wTu@K?|Clu~(LXXrigrE*s(*JG}_ z`94s(WSPM`OR;WwtKuQ$+A4Oy-Ilv{!xGVHHkU7xk@A4{{{2xOzQn_-1z7WDt7Au` z`Aw-H2*u8>C>v1{6uZjLi*X8>khQU&e)m;?RWQ)C!@4gB78dwn7Zry;4f&(m!xTqX zn+CsCqaDfZj^hu}Hr5rA&fy|sB>+1G$O+H_;b_6lF1je_Hw1^Di0w^{TQ}~|6zyqt zP`GA@-8*kvA=)Q^II4pIX>MIvj?5HE$_sr=-|sHvtLxd~`-RUci59# z{`e_?;QxNrp;Lp?aziF(HbmPEJrzNT0wQbyKq=>@8-I7>O`V^;@?wbk%?*Flrj|HH zTu~$yMcvVmZXjC0Wib#-Y%CiVBbKF*HyQvaOtNOlN%N7Or2JG3B}1H9GTFzyhLFeP z<1C~a04-2|xgmpUY)?x4ur9{yb$HI)5nVS0x&z_Spxv`TkIHuzYdzTZ=b1W^m4do{^`@e{1z! zhS17S;)a!@h^+sh>?O>$&0z#6>t9lb~@z76NFho zN-1W}I)tRmH`jZ)ymGl*K`aBYi$o(@zz(2GC$a$PuXI26==KFqtwP6OZR`6|ZN3Un zilfb&yno%XoQPCV>+p!(?LaRHiXH5(=cZy8+^BNC)!qFgJy2zA_zk4WLQgr!WFL*2&evhjh7DDL7i77jrIHpRPn5zaU zy3R|QLugipw$Y3ftg~;-w9Js2`x#dB)ynbz15`Z6%boqjGJ8h*DFa!@7{Fb&-rVm+ z=C!Qq=-fk2x(X%-$uinr;p(y-lvF8q3Wp9cFa(tLj1UR4Wj2-rf#vc(qz_=vgrIaiv+z7pd^&EmxhsV>91-608C_u zXg^~!%A`4?Dj$6D)bBcR{=J2&JJp+ww$pbgGiN_z6IR$}0$ao7<8g!V9(u=tm{nk5m{{kp?oh+ZHPB^#De?#8XllC%a+7kaIJa`7^7mBl$>d1xDKcOf z@6^5TG0y{3@SRQNMFw^D8xnpo&jPTHKVTFg0+sviRHQ$yob}ZeE7`efepl#ab|s|$ zx4RqP@*!{UNX}<%3w|x-V=H~QXPzk@gb?#iAi5ZH_9|OBCH*-bu}qnO|7?y66<&V; zyE9aZN~;Jx=0HTmDiG>yiLV{uj$!4g{20OAaBt_-9VlVvdA|zC?y4*gF<~YgPzkKJ}Jcr zKY%?LthLzygp!aKiO|!!a6%x(3A1_G`sW8toyFE?7s3s>-MHb zARWgqU_gwg-hO=>aa2i8L``?vVV?glANG`^E*w9V4*27|%5p$@RTpW2)kk@h=ixhl zgfi1ngxNB3^suZWDp%wAK>OL_Kt!yyp65A^!_3B*FgHjWD}^SsJ+ z;e-%)M%M;y{+fkv-8XT>+<|ptT&hF9XjzTnt7MzP$huw|Omfut%DQ}4&9JJ!OwrS> zRnyZ7cu0`0M?fV$q4aLjQve0VU&<8fM^DZU5(t?ME#A6?;b#CjTR}vzOE#urPF4ph zRtvI9PX*$8Fx0yZ9+Vu(yj4v^BxUTM*;laYG|{cr>3}GKCFsmo#`FFqXn`%hpZja& zHrao}6x2-SJPT9!Z&=1(#9Fb&rYr?kQU*zj&b$(%AJ`=JWFR7~wc|LB81rBoYawR838dh=c`zRncKlOm;DNo|i^arfWUH6U}*$`haIA z(32g;?Bm6FsvPQMG6^6S3yZ5tI1}IRx!HT<6thg3;DEiyy`=0a8UrEl{ zWU@PR-=FK6duJUT6OM7Y$TC%ow5aeXl~f^0L?{cyw~%Hf zJ_Kx~btg@w2lOcxa$@e$|6QWaHxZu3Fw~kW#zTA;vPxj2-}lOIadBMBR6-}!OK{G_ zF<&iLLRUys{QJ++!Zt_e`#yiI-f!3oCxwhZsWiNKlK6vc>lk!pJxNABrOM34KN_oc zU`1lJJ3hcJ1w?F7dP#{!6^+$yu6#-}%5lvZo2_ABs(RMz;N5&p{*>KYEMzUF6f_f6 z_fa&w-7r{nK%ae9rcsA&-sEI+?G3pFuB^=y2bfQWG`X5{rkJ8>(UN#ZcKLA+E~*mr zrBw4|WXZ{5tA`u%7by{jATVzX2BPWa#Zmc1o`)3P48{`o1I#75P3wn1o_C3|JNYt= zkJpQ224)VCi06Ysi%YK^X1{tVLy+hAgD;&wJl%tq?KHoBscW*qepQMj*E6DzK(HF5 zGZiqd?BOOS-SAK9=QZdyZ%8-^yzSL#{e5{fJ|(i6^)&ck_~s2ZW!9vbyuMhFJ_d4; zRWGVUb&sbW?7;#1_>~u1m$b`Up)6Lv5ETW!>%Du!@Ff~WaIXO8g3Ko5 z{({+@$&ihhcUm?MKViR(hS@kOLbx5o?Gz#et;zL-RP7soCB5S2PFPe~AIQgXRnclP zZ$!5<+#7c#am+Vx<+ky#D(}|!9aKrjQT_X1f?LnNGUE^065SnLFYZCB*Ljo1G;qYW z?GaD`)({cbjK@NZ|F?%j=O}E!PqFJrm|4Qxd15Jbf202zj#$TB5%FO!s091+EF4s{ zU6*e8my(h$#sMqK5v}^-<=tjS>W$-v9ou`LnfCJIfZ-mq^u8&ir*VaTd!~;UjxMk4 zg{6jymiqb~nuhXls}DQ53iq|~1hO;icu06g1|{1=qR7wxt^UxzphJ@&XOh0lp#4^f zjZhRTUN7fR=W6#PB1_DdQ89(ObA+W?a|uL4a{pvewPYN_zOac_=6;?`Ac4jFM*Pbo z5{cH7Pi7t`GS(X>Tj7iZ_aV|#pcbQSmS9%W~# zF~|4Z%WJtMW4=+gk3)FSypsNQRYXWgt4hxSzJ2K@;yia!74(1`i5Ztisuqzwor6ox zJv=rRbXY%o@}jGRKyeJlsQ315)w`Nd5}CA5r#<8|Gc$N-cT7O^R*Tzb z^&nC$cl3oBZ&c%=9#;X-iW@#hcWvxVShjqlS;(-e@v`fse&%nLmE~U{pTyYejk;-aZ@$ zNxS@Mdbqn*h3RT)y4{?tVc?R1I+8S`4D|F;U@&0Bh>ME{-gMC{|MVPk{2F)?M#E{` z#49E?zjN;C=?VPO{f_igUUuL5`Z^pAKR9q!(24^~#^BRa{%Dhwl%x`NmbqMN@jbuz zJ0&3@VKG!Ae%$1ML#xeO>lKx&Ja3Rwwa-*4QOgH4x56*%EH=;yx zZZT$M`lPPrtjq!~^~}u73=IvT(5`%iFSob1e*XR?YMEDmK8&Hr0G|V6*vrey+xt30 zz^?78zN*ro*7kJ02MUFnn3y0C`1tq@_4OzngDD(2fwzA*H#Zv^8UR0a*>l#Us{K(s zeN1@|N(L$|FE1}GO<*i*Zf>rsa$Tr%aBaOL+v*2)(=qe=Ta=ZRm71D*baXU1sr@OY z!c|pus5_ciD?mtXsJ~xHRW(z{DK(#UU|=96C8gGG?CAJd+V^a8BvS7e`5v!ZwM zmf=N=&}Z&8am4pcO;3jx-)N57g6y4~_IGzd!I%59s<7$t@e}0AgF38`ltW+l?c1ka zMQKE3r9xWJ#e|~M?DvR{;>h;Ezc~;FSP~ zm7U%1a^`JLz|}k&CO!aA_owkeEpBU_*X>UM2C41Wt;Z-J(;!ECdsbG~cTH|<2k zt!mrAA9P)YeEMW=ZvJ@xk2|fru1;4gd2nzL_#Cw()3q(*+qZ{qVvU2vMuPb4*$RV= zQ=jF)yTfl1-l!eG(KLR$bRThMufK{cgFwbBp8LP^LZ$9cx)v4;4N@mawh(&dKaofq zgXQ)h>1^g2!RJ^ZM{+?(=)Fo~FetrIE-F40BcA~I$GX3$!1pGRK`~#U`2AwTnz}Rt z0~!bd9s}kLcF1oDn?aI4u`5AO0T&w!^-kV@-1{VsThKs(?JN72z~p!dIy*l{SxtFs zDr@siD!>o;n@mg?C0vR#!y_Y}&BhN859d2$isAOJ%L@xP$Mc}MnOQf-K~HxxZ_U=~ zXNJ;v&^ofjJ}@ead+w2VNQ2yf9e?)r_U?|5t}AT;bJ;q@Z%~4PfqSuYb+`zebXi23 z2ILgoz|8mpD1*?#0OsNO`T6z1{P_S&PRrBdooXs4U)_7rG31F1Dk>^Ae$LqS(s3X; z=yfbb4e*u2!^1H#I0qr)P2gr4yiG$w&C+lm&?+#acQxudiX9e8wF*cS=)7~Yq=VXW z|JTaPalOYQA|gNuFxc6}lm^;_x1Fu+o`k#FR2+@;_myZ~he<|IlgAd0)Y-ECF_5lG zN=neyx+bTl228-Mt*vun7?r@V$GxoqPH;<0OJ`>%5MhA!oJ#m$z7})j#-f2YDzg&} z8w=|XHw6X7JD?^FdE92CsafrJ=_m+|xcZw%`U;Ok0HsEZ>`8fqRRuP^vLbESU9AzG zdq@FPHn=oW;a*={fI3n*-kN?oRnTs#s*2AO(EC%Qng%i-PUpMn?@wevSp~v{=p1S= zFvjvXl(1sTC8m^YinE|7%vgvHvcsf=qOD!Zt4@U>B`i zhSOyg74oPIuVd2%9VRyHdW8}v3AAqO>qGezxKq9G9E>F4$W@l&9($aJk61KE^ z=wR#XmYh0zdKe&JYEk-P$Fx?fG+`;j+yWLC78Vv4fo!ezJFR=U)jtys|0K2Y-2@3F z6y(pJpjF@^)VqH*?*88Zzu{mo?{W_qFWK4IdwY%ssqxfes3AV|A|ls4xSi{2{%*GI_X zY!TP&u^lLf^1Gc@|LfG`Agw%)mJdd_2$Ds0GKD)Ya8hS64SQTmds) zSxqhApr((E^WFIDY~%Yy;j;i*T^#la=dnFpo|f_Di?!IZaE zrcBW)(tj)Uc(b7!FyME%+={HMOba6xe1es#tPYilmBz1;To_o5& z??ti1)%%E@ri#_}kB{5k*5vwamV9@<0l@I|c-_t?pbcuhjS;Rz2_D+|xs&Zbnl0(S z(feFmgBN=NFEJ|%0%T5r;XL$S2MtV@>9m^iaKpQ2+aE8D8eKfSy=xB{hRzUI;_=y)YeX&eAQKkJ;TIIg2DR9=xx{jy9$(%OC0k$8>1wxf{xF+(x7ab%edF0 z$!MA+TRBhnEL&W7E9-(&=$G3p8rgV$*eYJ;pMl?vOzK+hIlk5raE)w-h9xH@K~wsc z=W2ZP)qOhoS z!3~Qnx#(sJ*J=Tue#7tr_6%}-HNHgOwQ*sa&+kk{L+ta5iZEKQzU0%eB}j%0;z#jv z@=%|Z(=k#`d0T63qZsXQz><;%mX4;oIx?icGAc8Is|ThJTHKT*uj(}N2WhKEKKmxb zX1GH8CYoYcc&HZ@BN?DSa~>Z9rzby2<&c2Lu!0<9X(JOD2ev%u2&_eMF}pI?t6SXk z`(9a7?4yJIWxqaFNKx1>1+<(eFlJ#^!a!yk@-wU!K0X~-r+bZia|d}+lo_>{Tj2v?nLX6xbPl#)=tZQx&2p`A}lTaWcLMp z#WGk?Pdixa+s)Q5TE&$}yRHM-lPw4Y0{!c=xyBaSQv2dZ^9-3~uhkV~0Vq4}Chxyf z^{0K*%Kl*OjPe8toS`BB;?D z2;ooubhCz!r4xms4u)vDeJ*4B{CP(>WKCO>zJ*}3^xfW->7;&Wsuwg-Ue|{-AiFKiX@PbvTQM zc^x{G_h&=XHXqtflI5(heYro5+_r^lWgR)>npGT*MN7fBSACJd8*bHUMc#QDpq(R)i^ntn;4bH11=DGFtq&vOOK+zfG>X0WOp<}Ul|FCso7XM(DY-1_0M)s@Xq9NONn=-%v8!ao9 zWQ$QDF~PxB`@&4#uC$<+c>CUm5iVy*ks45G@2!AD&Yce#8Giio;Hyof>_q+K#njBY|j4 zKM_j5DNhKrYIF}2+l}6@0(PVKiz|=%hKB4zXFziII{M>#f8)MaSqz{jAddb3Y|foF zv~&LOZ!8GVIUMT)BO?#M{W$QHlr1l$kQ&nGbR7V>_WLu{|JW4(-jTsUZIZ3si2}Zg z9|Ck<^jH9ec+qwxV-qvSf+cU)qG*nQ{O95_IyeZh{Zu$MG3&q*(t9kOuWSEOSVZLR z;Sms8_v_^CGO7lbmzUnlzIGZKV?cHUVrFe^ZKKN)4hRU)iw*CQ8*vhV?%+0V(lj)D z`2ZKErhi66NZ7QL=U@mVDWFsr7X%1=4*iOENG||uxX4&JIEIFYTR#4soGemhQdUt{ zm-0DT9nKJ7QG+KZCMLpQlQT1y(>fX`!F?NI9-B(~`edLAgWBGW)0T&;MG5aC^k4wB zrEgZl_z?L}I!jsh{i zaOwlGe@nHj$ZI=Hp$r4^Tu2Z8EAjB4yH9O=R=uFL(%yI)0#N@xEf61~FfcHZnAK2& z^~&`nZ+8oRaBkEFwwq1Woo@7%BlfQECVnk1FJsN{1^&N|%lD3tCvXliD8?}osAq|4 zP1TJ_-Qm$_kzhP|H5>PfS;R+V^)-m^?9VAEm|Fw)W{^aG*<6?DJJBK=Ddpk(*!C2us)-w=S3#^akm>q}0-9`5cS{csewdj5RRyCmDJki$#+e5S zI=P=2JpA!9I5BCWq-=GqMvF;pDuE(q%A|A$b2V0gRd5Z65rBA%jEr=&wPU_~G2xyyd%B$k z61YW;%}}Z@Bs(M|1b9(~`00P`0xPVksrmWSZ+kccAd`Q)1@QpeBqS!f13DJ~JFC99 zk!;Dk2}PR2zkdgYhd<&_J9tF7UctSu*R?T%b8@VV01GjlrRonmT2+qL&5tHD<}Cq!Vdwu zdOZc=S%r#If-RR~M7xKF4}g|XRZ#(C!Uv#vc#tTFdG1XEOWKJYfLiD0=l^cjZmO-V zt*cweIyl7Beh(^c1u;M;H@00?0(t@y>|=g^bGkE@W7hVmdFA-{ z;NU+a!prLl5LZX7*UJFe8@GKDlazb_iaJX)avslycfA zaB*-bvb-0)a^7jHOzCwqGJ}_2%0^iKC9Q7lQr3MrEdX`I4U`H>T^uF93oZ+}skT zrW;PGZEk!!x?>9iI=UknFN%SC`qgy=v@P#gvqJjcN36Xdg;`_FzoLzr(MP-D!e}X(K*3IssT=$?)ll) zg%%ExjbEJGKtD8M&>}2R+?i&{=Zev$YKHC=x1SAih>3{-&XeuzULWCf>FnO~6jXub zSclA;2(sV|kbzT4d*aKpTJ{(FA`PsBvxy$zg5IHO{mZ{9CeB$M$O#Ctdb^5Yp6^>tj)d z-C-lFtW(^_Tl{mGHFRoyUpz%UqMC<+@25h_oyz@fjOav<<@9|}zVKcvQ398Z&r-XN z#7tvs#DT}=DN#$vh?PDIurS>dq3l@K_U4WS?{1Jhv28@;Dv6cE)b1NMFXId0m2sR6 zgWq46HPWk@oIT)b^cIW#BbMjm%m_RJ(c|Ko^?V(*hlR!WbFwQgyvQDUj(C|}^J7|^ zLyr*j$UtMxg^u_53uyBacQ0O18gT-KD{W$!hWdC*ZBMdus&Z~QV~U#Sr&Hq6Va8*UcsWfq3-ZgUUjw@Ih)sOJoKBM zFJmxE(&e7=$98Ve01T@ePn*#< zQT#rN*P*3D%X^S+aIXF(i}^G4^O&VMz|!IaJ$m3ZFTAXu1P-GfmsB`+m5qqN1Tg9| zn8K*$5MC6EPF)JKlC5M=38^Z0Dd+Pa@|2=hsb+X;+&JSU3!p$L&yov1#^GNK4f|Z9 zjBWkTd6FmX-&zeC`N@8@jMD&OqwLgYR^Oe$@~wSlh4#72_hR%}JbQml#Jq9&|C}|g z(6!M4LP)||CR3!jk7%pDK12FAJ-9-c!Dqb!Uf|R3q<{y|#VT#W!;9rUjllK{*v|a4 zljny|n(rR!!rQbVZ|92|rmT6#pZ6@jQL5ebUS{cZaR$2TgA({e(#MBD1Om%FyY&WY zQ{&}>yCeF^;wS?JqqtAeHgk7~0I8gzExDp}Un~VJP{)>~XJm3LqcQ@qcc*jq$|6!l zNd~t?l;`_?*jo3K7a&7EBqitl+bx&+QTDtdh@~xhxp2w|7AV|TTzbM4s6qrc9Aj? z&{H;)h3TVs?>k3WjY0=k*{NK3f4RfjK8o{X8KCm1Fx&sGVm+6su6t{-9f2#$RFc6; zR4jaOEUGrrk87uQD4&9oX(*&&+El8iHdO~O60pBw!eit|>C!uBW#VD2R$z1PEqcmY zmUvk3bIL$>V66hiXpD55-sY<(_NgwBt#$JmRZF$`I_DMd8~lx&8~jV44*SCaTkiix z>ZG8|bgCN*up5N-;t+-JsTY6fKwCnee>!I~ zpnEM&%39DGOz=8b5O`jI@_)|=*bb3%DZU-ZvOhcdY@n@7lX_<6qy$rfFotnKY&T^c zi1i{dv%Gbrbx7L$*7`LAB=;H;W`7ZXEIscm$Bh*tO%BMY#E!>Xu<0P{;y>6c;7(Vw zNn03Aq-$(V^hXYuP9H2-fR%6$V0hxLN{`HTo>rX&m|_vHP+5i3N}II4vV zcfXM$LVCT|u*Ua--}b@d(EEz1s3=&Pb ziyWm|Vqv_yS0TCl6tZP_Nf?^^SaDku@wyinr|1MvX#HGq+rD{1d%#l|K*|d0^55mm GL;nx|w6Y5T literal 0 HcmV?d00001 diff --git a/docs/docs/model-host/imgs/remotefolders.jpg b/docs/docs/model-host/imgs/remotefolders.jpg deleted file mode 100644 index 273d07441b14716420077c9bde86372c00157b5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67429 zcmeI51z1$;y7<>HAW|Yqr%FmG-6aA7BHg7TIRYXfAq;|qNC*f>gLI2@D;?5EH%Lkh zIlwUd$FukL#IyIk_uPB#x&M35!e>3Wf?4yu>-W9i`+e(;v(d9z;G&YeqC5bBK!E$0 zf56!UAPby7cMkU)&Usv1Ts*w<_=KcHgaibHRF^IjlhRQ$(9==V(q3WWX1#KalaZE| zP3Su3O&)%Jeg;+%31MDwZa#kAA0Gn2!^0yaAfzNBqU60wdzJU^{yF;uketWXInM-z zTm!I3AW#y>Su?-@01zC^xBc;jfBk`AL9ubp;hx9CC%{}#b`ijWK%rRJP#hd=Y|Pc3 znDYQO3C^XfJTm7lt3SZK=0M8(B0TjxbwT+{bv&%DAH+SEce*OW0uYw{X-$q5p zyo-%{pO&7HnU$TB`>~|7th}PKs=BeMxuvzOy`ytraOmss$mrPk-2B4gx25Hk)iuP< z?%w{vA@b<>N4p>Z^p|G+Y}wzmiv-gyENpBjHtvshL9m`-HYf=;&Q+dsmt@p&A2?jT z#{1$t>8Y$i51;nqlQZ$pF(ZC7?N2THk2TEaKh?6I4f}Ju#sNYo1oQBq zB!CpKtHk!+8z0<28GsCc41f%P4E#F`aJE=4H8XheO^}`eAK-(gsAp$Dkpikos<%Kh zg&_I<8Iak4+&TkH!hW9BS^-ahBKZ{+wQ+b`rjvZzyy-{{dj>=X>?}W&$RDeT zHJB>$NVV-q4aywW`8LxrbgwgR3oU87jn*atNY&-Q?H?=yqch;0<8){NyQR9^{C6FPRd_+K9e1;B<0Y-O^WkU(mCVX;g4oWG58%g<}$ZeN{BH zV9pjxLbP#dW5}lq`|gSS=Z;UaYPi?!x6c3t#0@{oVeuOt?yMEP@zx$?@mDN?Y`SWC z^w*{RjU;mDT-J+yf)MxHKF8b9KE1BI3^C9bTO)nWMc4XEVHpeiUawHIqqM#8!&?XS55h--AIQ*1Zz>Al^ftl_ z>1;+6YZ2lOQzpga+0o{&y?K?NFW5Uwdy97ljekY)cxJffniA*6$7j4zV2G~V6#`_a zj?uKk(*?63H<}_0#@Fi^cWz{=^%51tCfp)+v0XpLvVsO>0L1{7;w;2i=oO00ZLZ4$ zwpZzYqFfDpc01>i5bUjBb~IFvTe`i`(~zZ*)0M6GWH;N;HARN}JE|t3{?0y)6_i-7 zUw{Z}Pk+q?E%#Z5rF(7W`pVPvpxz=)WXZW8v;fO78jV-qO*w`Wp;Wodz!RmqYGTS3 ze0QG3GLUDqr$&+P6_v2$n3MrvPBzK_<&K{lbxyvxrl6vnj65t?hKpL`77ldc((YId z)t*Bjty}}-hBUPXbtN50DAQllJ&lzR+dWppWx%K)?DB5Ait{7t%A5ddmGlK=m<9VM zFZnvw7BBqm2LIzCt5j)BJx^O*H8C?;Kv3Lm_$o=~>cS=2y0j_B zQ^H)UauwPs{suDdEnI|eEs2Am+tD*t&R?z2puJ7M#v0Q5pr(^jO5G^BB2zI9_N@AG z)s!utLgXa$GfJMwX=BzfgzpoD*VUoax{pIwE9EL)ZO7iyvSXK@eR@xXjwAkK$yagp zf;M}S=pFR8;(A@qd$>AgqmW~@bGBsTHj+errN%lE9GVEV(7hK+M}jPOQq9KVU8S-{ z$YSSiL5Kr*3u?BBv?I@$N_dx8~0`Zhmu!5Z&K0d0dy+Uwm_(j?YBfn=xgZ*}_S}57X^w0#`9xl9u`VRhglRm5&`Ct3{aTd`!1g`}_-&B+ zZH#&SiyfnmF?SelAmQwQe-K*=*6(c@6;|XN@=GFC7F7pC{eKJ&itYok%3cUwU?w?bnq(tG7rRBQHD)nvFcjvkW%kl9v%2_>PPmJAevhx(-hqUYX zCwiJ&r5jt{G>I4^V)}R$I{58Ahir4w<(loa&rd8u*IH*SXx>!|Q7yU%7kYV1K?aLQ6yEQgsehW$ejU&#Am?Ak@)xg5u8>TiGlbjk0LzO zpjL%>YTF^@ezuRDrwqT3IEFM1PtWu`GSv}#OFS|mU}&(NFm4fhuE5%v4jm&z;BYC5 z-IGdHRS8(mF7$2kx>SpxOtM-Go$i^5EoAB-Xng{#IcTnW(f02bsGf+n8OLvMC9`## z6*FD2^q~z$1_4WKdG1Mx7#1`~joB`LnlQv=T$3p7wwrJj>)JkEckfBzUsvQ=WDD$K zs#^7H$Mn~R*;6t3ui_keQ;KO51z+Ia*a|)yuzDY5Blg61rJ#U!%VbXv$@qdHK_}l#A z_ibX6WrUPkj8cE?^=#=o5z|iLPaG1iEFe}*n5_w)Q7DpDs2jAl#byv{d!@=qy(v;h zhRjP5yGAy}c^1YOeA*p*{v`~1?NQ?bzKfZZ25udohzPj%TCI3MT-yRF=Rn=Yctf0| z>H?hSNaH!7Kg*CnPCNbMC#~5I?bziGNB9M)t|c-J$On#di5_a?71yh{66Ydh!fFG) zY4#!Z3e7&FufwrjqYAG%)s;AWQ(g4Ol3S0OgtQ-Gd2^UH&5a4r8Tx97=tdIlgk zgLf2rQMJ0V2xuF%R8Xl3??5X}zoXR*e+9{Vf8$Ewriv%*^(7Bx3v>-4)k+W|CFNC) zoaH-XO1(6fBS>HP9wGFTt_?XE-&<=^>7NU2Qz zqsU3XE}3M>d=a6I6iKPA7nc`K$;aSVWChF9suH*{q3F77s7d2AUcH13ud*+bpv=66 z*ql848eDYe28}9LZ%A1zE4I{lqMOJpEpZ?I>T`t!8V{*g9P_s4jyc-ju|2}OMwbF~ zPP}#A2p*zmwZ}uI=^8&AG@^bKBP`@HqBBq|-^Q$T9al1z6f(48Q!xXSk=D>eK&}T? z2(wSbFocmUp$RZe88sL?=mRM07&Z<%;c?0PNOANz3Xe7yf0DSr_cx-hs*~ z_S4cjlb+La2GfsX=qp890GAo6GhoqEYJZ(!zfefVUo4Eo`pp+ekaA`C=T)Cs>OBTB z8+rnSW#Hres{Pd~fw%G;Eo_;11qTyTy(fAc5=ID$)=6wkn36RHHzGP2Zc;LaV0|YD zz8uQessgy1w(<`wjyNIr3D(y=quaWaJ@})Z7)D02-`^I~QGJQBo=YZJp^kprojaYe z%F1O)``~GfyIU52^~|eUv4su%FY6TD>FX}AQlp{TceT2(Q zKui)z-uNLK$FgQ=;gQZ_<68MImwQl|&cyeu#t4;p~NE2PZy$dfm6( z9VaL7y-Tq0;m5TMd9dzm1J{tp`I~xO0=4Cl{KY(re&ZRcUMP>jTG)c2vEkj~ZS*VB z4=V@kSf#b%>4aDcS@4{c3-oocg@EH3%m_AuCv~8-Yb>Q&T6^nIv^y%4hK4oN;-Q3m z`>59=r=nZPuzXjKmK|%`EM!m*k2Ou&>3f2x4l#$|+nz+g(;1?e{rcGZ|ZY!Fx$_J*;JnO`+cG2V5GW0Fv{TZ-hXMOf#I(#tuwf7~Wg~D50 z!MQ3zqo;t$x~ehoGbI((nrnORcteo*@ASo!&y~ z-k2q(X`55-iifo)d9rNV#%ew9bhm>_CdvWkqsc_0Yvd!a+_mq0N*`aG#!~hS5Sx~I z=u|W(Q>=R3Y$&cCwB3Avv$?`AeQ&3k%BsP^^YrlN%E;_o3<=M5(;2*fXSB8sV+ z&c~dqeQ9*>W9fTq?6AZh%5NnjEOk;%aSZ|d@PP527gB*6^qZ)5^|Xpgp29_Z-n(ua zt0wkvOPu%=UyZVQLOd?JkKXldU*t~tq{*L)6)4ZmT-0CdYyMbQ5dyJEX0iyx-jYsaM?Vu4GgfN_d1*Txx!F%UPs0&y=};!c0iL4Y)iW zqN}uBFr2%)n#AHi-l@y^psFI)5MWBZLVtC8e9U^hpuc)>f#~2m$*VA` z4yd^AU|44r^(N}s{FIc=0DlO9QUvU^u?PUmiLs9I*=Wa2a9%fJ{$-+#dA1DKk!Juq z_ixJ_a=)yFWY?SlVKFGFC_wh#SmnXZL8)WZ8%*XH{@tf)%RhOC7c(M`1UC#6n7W23 zAyi*(@SiGuAGHzG+xg}iyAT7V-M|hGc0sL6sScGE5sVrunz=X==`3Bw@1T7gE}fOr z<*kRt@@ClXMJv2kJ!Coq`cy1khawjwbXlbu1k(QUxFN#W-}l-4M2?{$`Ry-qUw9|l zzvl}x zuRXr_B2i}Z^F67y?Sf=F8+X=M$*O_|-*O48#u$429TFBY86~5d^-NXL){0FGlnS_N z#f5k_tGM(j{##(vyGllr#KF0BOPib<~9r_G^>Y0CW{q! zTCDA62@zGXpJuUKq5OdB5xHt$4BPVg-iv16wPDZZcT4Tl#BDyV12Yg@{rktS;{HchA%A!E?>F1}AN5=1zo_JX zH0E4e07~vg05^IBW^OIe-mY(XLCNQ)G{hU#YC{Lp6P;S0RyK#(iw7Gh_`-Wsvn{T8 zQRa`c0q=wUzU{a9o7&K?>)rY>8fS&{UW`PWV41f%P41f&$t1_S(A`}@xCf&vJoelC<>!}Uo!i3c)D01{sT-eFI zU9<;$Aq~Zb)k0?a=RNdb?HQ0XgIqZS@PT4E-M`$ID&3M=u0V@QHF|TZYo7r>cf&Ts zdXF~FfY7k*6arFh_@8&v{`|?{eL%T^yn+1()H|Ra-3G@6%=ij&0CMod&H_0AIruv` z(1L1K_4afw^uN`9H!YUVMLBjY_@nzt5MWEHE{*eh8(E)~7sJP|r3JSRY~?*1A27tS zb7e=eyW5Jjs?vQr9*WcPeGj8x8J;n&^#Ar^Ct|7jN_Y+1HQBIMZ;@(tS1uFS=!P=W zy`A%2N-ntx3z4H*(y4LVf(D?e zCbaypX$5`z_c}&^*Z+NvB>yaV<#2twc`pLmx4W0Ul0xJ*#I%`~X(Re?2OYS05rmjy zBJzUF+#<+ex6dg;$xKvPtyp)?WwV5s+t1~lDskN#-(15>)$4AE%jArV+-2WCcF9X3 z!A6?3yu&^NQnPjiI?{LA2;Za!7b}OX5EF&Q9;@MOuznX3ed%Cx)A-(CXD&5|_q&e8 zgfz>d?40N7Sre;XEDgnF8Xsg?hOieGpmkaUZo`iq&HyQrGhpRb*>lIXkTTQbjZ++e z{v5`saY7LjW-`+^tW$6XTz$QDjEp=3ZVBYg+8@!C7GW+UGPjw835}fr$*-jL7^Uf+ z1!VTeH_~63$Tac~Wvg8!8ohCat6w$7kBVvq7E%LKWIfl3Hy=hh!QZIUJH(FGD-3G_ zI_B(2KY5oH*kAUeyQ3o_Grp|nBbKuiuTmMPO4yfup}i@Tn5x%c8j*3j z1G{zHGu`7sFr?sPF*q0FFb*;P@-i~KFJVMvo+Cs@zu@Xui@+{IfQhHM_z2(aytWqL z;2Szd3MtBWb(9?_cVZ=Oisk)Xzrg3@@~+bpl2aeYk< zF?32J%y%!6N$}(_Yn6UYO!VlY)W?Owd%rj=S)`r;^urC?QcKtOx@FN41@Kw;7qxSq zKeVE|P#{SLQen=u&phs%H+}k6kw}r^2#X+Q1Qx%3lZt1G0y;ty0;uMP;Lc5DBqg4{ zvdmo@yv>{h8@iB}EbaMuH8gRpydcwRntl5&nt6a9xi>LEG2v`WO!8sjjWE`np@9UK1l%iv`p{I zgeHYZW=ICi6=5TvsY}ug7TqW;2#0&(Z%DCme&N{_x~G~`2v;5!XC!q`s>rsE-dVfB zdd%{+_=6t>V3|3i*_)+Qve0{UNgU(HZvsDI*J<#v%_u=Wlgyi&pz-MT=(*0aPqbO8;ah8u=@MQFTD2X65GNZw_aV0y;fIJvGYm7e^&Hxy+>3xy!v~1q{#T4ZjTwC zbVStD2jpzxyBc(*v*jAMlYh zPClSdo==3>p`?hz^Ym}yEG1D!@`j3!?siC~9WOki(s4SywXR(6f6!^+KTkdxTG!C* zp@u6r*Gpt55Q+3{=IpcjAV`Kw>~aQ7s@iMrA2KF1B+(Syn)iU2m-#CaFyS``eikQ; z8$hrI2{4eF6twK-=EKT`%_#4jK9Nv$)GLLQ{B%0Z{LLZ!Z;XInZ5oOV=Q@K{2u@~> zhy8h`z`05%qfU?gB7R#$4et#LT;CN?b-^7`Pb_# zwT7;dn5^l>@c33T4VHL&K2gzl?|s7nCYte5QY9(F&6eEpBh?s%WaYV7w_F?1Kp9WC zv(x3xS4)-?dmZz+>e81DrY6Vkv6fawPpjQzdewOoCCA+TwPkV1Bj;O^&N@?}e%QPX zI~&$UW>L=Q?S~~pb{lD|%nuUZT;4mE>T|p~zP_PmtV-6ZxNG(%Z+9fTTSgABR+*=> zo2T%%HB*aV#WAM;*7-pqkTqPt^jAxc_Y8KI!_L;qI;$%^xDsC z+Ow(5$J6o9J;J*5AD+GeR22V2MWF;?f(#94Ptd^M8QtNbe#(cE_vSWs#Dwhy>||T( zzb-Z#N*MT0Jz{KswKa09VQEpjH7RQQ7|Sz2vf=3Bpj8Ua&i6B5dE^YZMlD_=b+ms5 zwEIV3Vo|tz(Vp;~t7%;&7<~< zp@>0_OZW&bbK>^3fkT>Ourq&&7)RlPDl!1_uj zDq9NUv+hAc*Lnd4^$7$+r?&_lt8&HAm%x#yvYb|r7&{0B!(=NRkvdNAc^J>f#)|P3 z4!{YMmyKDX9v<7(Wsf&(8+4Q&7f}kl&a!@Hke(mie$$ux`;%OWZb0LGSxVodnut3E zHy&>?%S2N1VR^->5ie`+ZE&l|s+~~0d(#!cb45Zy{nPq>3O>KC^fXB=UpJ|c;Ky1$ zW}!Eu61+Swj=n0_VJ!``6k!kV-YO^#=Ds(W@C;f9n5*l4cZ!c+d6an9!La3UDe6Po z#L(bkza7r2Iv+i|f|0FKPDR!1DXf(luIC)*6%#5tA(}%NU*gvnh$PHle z7DPOG%Gb(4UJ$$W_(9U=isp!mk6sPt(1`{ImAoIFFrG2YKMZe+1$N4LES9tG5}sN? zxr0{4)olsZBgIS&n6t% zdVQDKQTmo&5PQT}>l4SYFBb3nOW_-@ zlbP+(UU_%uXc*F`1$&i3V&zmNO5{A5b1mfc3C*Co-jA*GFQtM&+g%4Ht!}bLlQf;d3LYO}CO?3`j(ZFU; zfiIwj1^UXSs;8}!nIf11S)^D*Qg8+_38A~3>3T>!eid^j?l(R?WNw50dVX~}016HLeP!|I&>SGvc?|jk> zm3K^DBPITjCR-L_cSoRcGw#tn@3dma^XpyhH8qUtKIXw z+4Rku4_uZ9T^z$2y%`Y~GHi3#t}Tr*$$a3z1F*xBZ^2j!Y$yuguDfsbK7Z0O2}(=J zEd_MY#T=h4Quwv8}b1fs%n20mwU^W%Q6wx~2w+^pvyx(a>zQsLk zx5FeP{Z7#_ls}^P8q%HCx^V1;&CpW?zhF4ifiv`G`nA|iDbK5FW1qegmnpo5b!CoC zjrV*M*T@ z;31>&03ZDsAS8qAJ6HZz^#EP#*n_*DzixlTdMLGzc}f%wSXO^C!66eDt z!&uU+c+u?h#72NBKP18-GBmM7QH~T)2ec ziiN#vIX>FHP@fBOPSo`U>Fb0=OhRg3vVkZv!)L`tVaw9g*PIbI{5LbIU*SxpFq^9f zBa%l3s=6&>K63RG_O`$is;6>oNt6Ha_Lq308|s^E5yy=as+Hw~3yE>FAv^a-$<#*yRg6gL zsWIL4neG|z1mi8jl8d~7mTf~x{|E86w3I7Uk@@8swHDFyA1G_>Fb z%HZE+AgeXLqXR$1{M`W=cAU3t`S?u$El9=U&GbI+46vSt_u8YJ;C>9_&ysg1&Hy@w zIcajX+%w?M`w9n%%cuiPHhl%88cq;+!L$5haH9M#v{=Bd z>~{BZy7!*=DZ3~e)WjK2EPFLB^xyG)CmfPm7#~67qFOA6{eK3za7g~l)5~05mb5d+ zwUO$U=|wI);waR2p^OSg&*wa&53x-}`dW+ucL6L%pSPg9)aOnYx4c4jk-Eo?3n;@E zoBRm!@cD1I1|}{C76#IWqa_rcDDXoTwX~;n6&v~ms2&S&=yMgCN%0DwDr6s$DgAO! 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

S~Cf(Vy)Y3jfEIZhdxz;45r8kmT-8Qu+hXQ$#p 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 Ne@CJbV#U6l`A<)ovc&)Z diff --git a/docs/docs/model-host/imgs/uploadfiles.png b/docs/docs/model-host/imgs/uploadfiles.png new file mode 100644 index 0000000000000000000000000000000000000000..35b345f52a1c5efc76cd5611934145c3e79d2272 GIT binary patch literal 24934 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 JrCYUO_n+Gw%FO@( literal 0 HcmV?d00001 diff --git a/docs/docs/model-host/introduce.md b/docs/docs/model-host/introduce.md index c1c645a3..237c30f0 100644 --- a/docs/docs/model-host/introduce.md +++ b/docs/docs/model-host/introduce.md @@ -18,20 +18,29 @@ ## 在 ComfyUI 中上传模型 -!!! note - ComfyUI 默认传输数据大小为 100MB,建议启动时,将最大值设置大一些,如 `python main.py --max-upload-size 1000` 设置最大上传数据为 1000MB。 -点击 BizyAir 的 "Model" 按钮,可以打开模型对话框。 +点击 BizyAir 的 "Model" 按钮,有 "Remote Folders" 和 "Upload" 两个菜单选项。 + +分别对应了已经上传的模型和上传模型功能。 -![](./imgs/modelbtn.jpg) +![](./imgs/modelsmenu.png) 在 “Remote Folders” 中可以查看已经上传的模型。 -![](./imgs/remotefolders.jpg) +![](./imgs/remotefolders.png) + +- 点击 "垃圾桶" 图标可以删除上传的模型 +- Status 为 "Avaliable" 的模型,表示已经可以在推理中使用。 在 “Upload” 中可以上传模型。 -![](./imgs/uploadfiles.jpg) +![](./imgs/uploadfiles.png) + +其中: + +- Type 选择类型(当前只支持 “lora”) +- Name 是远程目录的名字,如果与已经上传的远程模型重名,将会覆盖已有的远程模型 +- Local Path 是本地需要上传目录的 **绝对路径**。如果你是 Linux/Mac 用户,它看上去应该是 "/yourpath/xxxx",如果你是 Windows 用户,它看上去应该是 "C:\yourpath\xxxx"。 ## 使用 SiliconCloud Client 上传模型 @@ -77,7 +86,7 @@ siliconcloud upload --type "bizyair/lora" --path /localpath/XLabs-AI/flux-lora- 当模型的状态显示为 “Available” 时,表示模型已经可以在 BizyAir 工作流中使用。 -![](./imgs/model-ready.jpg) +![](./imgs/remotefolders.png) !!! note 上传的模型,即使完成后,也需要一定时间才能在 BizyAir 中使用,一般为 20~30 分钟。如果状态不为 “Available”,请稍等片刻。 diff --git a/docs/docs/news.json b/docs/docs/news.json index 0accc011..a1b35271 100644 --- a/docs/docs/news.json +++ b/docs/docs/news.json @@ -1,5 +1,6 @@ { "BizyAir NEWS": { - "09/06 发布 v0.2.0,支持试用自定义 LoRA,欢迎升级使用": "https://siliconflow.github.io/BizyAir/model-host/introduce.html" + "09/19 更新模型上传功能,显示上传进度条,上传模型大小不受 ComfyUI 限制": "https://siliconflow.github.io/BizyAir/model-host/introduce.html", + "09/06 发布 v0.2.0,支持使用自定义 LoRA,欢迎升级使用": "https://siliconflow.github.io/BizyAir/model-host/introduce.html" } } diff --git a/js/apis.js b/js/apis.js index 24c4c613..add603ce 100644 --- a/js/apis.js +++ b/js/apis.js @@ -1,12 +1,28 @@ -import { ConfirmDialog } from './subassembly/confirm.js'; +import { dialog } from './subassembly/dialog.js'; + +const fetchCache = new Map(); function customFetch(url, options = {}) { + const now = Date.now(); + if (fetchCache.has(url)) { + const lastFetchTime = fetchCache.get(url); + if (now - lastFetchTime < 1200) { + console.log(`请求过于频繁,忽略请求:${url}`); + dialog({ + content: "The request is too frequent.", + type: 'warning', + noText: 'Close', + }) + return Promise.resolve(null); + } + } + fetchCache.set(url, now); return window.fetch(url, options) .then(response => { if (response.status == 404) { - new ConfirmDialog({ - warning: true, - message: "You may be missing dependencies at the moment. For details, please refer to the ComfyUI logs." + dialog({ + content: "You may be missing dependencies at the moment. For details, please refer to the ComfyUI logs.", + type: 'error' }) } return response.json(); @@ -14,19 +30,18 @@ function customFetch(url, options = {}) { .then(data => { const { code, message } = data; if (code !== 20000) { - const warning = new ConfirmDialog({ - warning: true, - message - }) - warning.listen(e => { - console.log(e) - if (e.behavior === 'close') { + dialog({ + type: 'warning', + content: message, + noText: 'Close', + onNo: () => { if (code === 401000) { document.querySelector('.menus-item-key').click() } } }) - return + + return; } return data; }) @@ -36,6 +51,7 @@ function customFetch(url, options = {}) { }); } + export function check_model_exists ( type, name ) { return customFetch(`/bizyair/modelhost/check_model_exists`, { method: 'POST', @@ -75,6 +91,17 @@ export function model_types () { return customFetch(`/bizyair/modelhost/model_types`, {method: 'GET'}) } +export function check_folder (url) { + return customFetch(`/bizyair/modelhost/check_folder?absolute_path=${encodeURIComponent(url)}`, {method: 'GET'}) +} + +export function submit_upload (data) { + return customFetch(`/bizyair/modelhost/submit_upload?clientId=${sessionStorage.getItem('clientId')}`, { + method: 'POST', + body: JSON.stringify(data) + }) +} + export function delModels ( data ) { return customFetch(`/bizyair/modelhost/models`, { method: 'DELETE', diff --git a/js/dialog/apiKey.js b/js/dialog/apiKey.js index a47205a6..6dfbfd5e 100644 --- a/js/dialog/apiKey.js +++ b/js/dialog/apiKey.js @@ -1,74 +1,16 @@ -import { $el, ComfyDialog } from "../../../scripts/ui.js"; -import { ConfirmDialog } from "../subassembly/confirm.js"; - -export class ApiKey extends ComfyDialog { - constructor() { - super(); - - const close_button = $el("button.comfy-bizyair-close", { - type: "button", - textContent: "Close", - onclick: () => this.remove() - }); - const submit_button = $el("button.comfy-bizyair-submit", { - type: "button", - textContent: "Submit", - onclick: () => this.toSubmit() - }); - const content = - $el("div.comfy-modal-content", - [ - $el("p", {}, [ - $el("font", { size: 6, color: "white" }, [`Set API Key`]),] - ), - $el("br", {}, []), - $el("br", {}, []), - $el('input.cm-input-item', { - id: 'bizyair-api-key', - type: 'password', - placeholder: 'API Key', - onchange: function() { - this.className = 'cm-input-item' - } - }), - $el('p.confirm-word', {}, ['Please visit', $el('a.bizyair-link', { href: 'https://cloud.siliconflow.cn', target: '_blank' }, ['https://cloud.siliconflow.cn']), " to get your key."]), - $el('p.confirm-word', {}, [ - "Setting the API Key signifies agreement to the", - $el('a.bizyair-link', { - href: 'https://docs.siliconflow.cn/docs/user-agreement', - target: '_blank' - }, ['User Agreement']), - " and", - $el('a.bizyair-link', { - href: 'https://docs.siliconflow.cn/docs/privacy-policy', - target: '_blank' - }, ['Privacy Policy.']), - ]), - $el('div.cm-bottom-footer', {}, [submit_button, close_button]), - ] - ); - this.element = $el('div.bizyair-modal', { - parent: document.body - }, [ - $el("div.comfy-modal.bizyair-dialog.bizyair-dialog-sml", { - id: 'bizyair-api-key-dialog', - parent: document.body, - style: { display: 'block' } - }, [content]) - ]) - document.addEventListener('keydown', (e) => this.keyDown(e)); - } - async toSubmit() { +import { dialog } from '../subassembly/dialog.js'; +import { $el } from "../../../scripts/ui.js"; +export function apiKey() { + async function toSubmit() { const apiKey = document.querySelector('#bizyair-api-key'); - console.log(apiKey.value) if (!apiKey.value) { - new ConfirmDialog({ - title: "", - warning: true, - message: "Please input API Key", - }) + dialog({ + content: "Please input API Key", + noText: 'Close', + type: 'error', + }); apiKey.className = `${apiKey.className} cm-input-item-error` - return + return false } const response = await fetch('/bizyair/set_api_key', { @@ -80,13 +22,13 @@ export class ApiKey extends ComfyDialog { }); if (response.ok) { alert('API Key set successfully!'); - this.setCookie('api_key', apiKey.value, 30); - this.close(); + setCookie('api_key', apiKey.value, 30); } else { alert('Failed to set API Key: ' + await response.text()); } + return response } - setCookie(name, value, days) { + function setCookie(name, value, days) { var expires = ""; if (days) { var date = new Date(); @@ -95,16 +37,37 @@ export class ApiKey extends ComfyDialog { } document.cookie = name + "=" + (value || "") + expires + "; path=/"; } - keyDown(e) { - if (e.key === 'Escape') { - this.remove(); - } - } - remove() { - this.element.remove(); - document.removeEventListener('keydown', (e) => this.keyDown(e)); - } - showDialog() { - this.element.style.display = "block"; - } + const content = + $el("div.comfy-modal-content-sml", + [ + $el('input.cm-input-item', { + id: 'bizyair-api-key', + type: 'password', + placeholder: 'API Key', + onchange: function() { + this.className = 'cm-input-item' + } + }), + $el('p.confirm-word', {}, ['Please visit', $el('a.bizyair-link', { href: 'https://cloud.siliconflow.cn', target: '_blank' }, ['https://cloud.siliconflow.cn']), " to get your key."]), + $el('p.confirm-word', {}, [ + "Setting the API Key signifies agreement to the", + $el('a.bizyair-link', { + href: 'https://docs.siliconflow.cn/docs/user-agreement', + target: '_blank' + }, ['User Agreement']), + " and", + $el('a.bizyair-link', { + href: 'https://docs.siliconflow.cn/docs/privacy-policy', + target: '_blank' + }, ['Privacy Policy.']), + ]) + ] + ); + dialog({ + title: 'Set API Key', + content: content, + yesText: 'Submit', + noText: 'Close', + onYes: toSubmit + }); } diff --git a/js/dialog/modelDialog.js b/js/dialog/modelDialog.js deleted file mode 100644 index 76ee503f..00000000 --- a/js/dialog/modelDialog.js +++ /dev/null @@ -1,106 +0,0 @@ -import { $el, ComfyDialog } from "../../../scripts/ui.js"; -import { modelList } from "./modelList.js"; -import { uploadPage } from "./uploadFile.js"; -import { models_files } from "../apis.js" - - -export class ModelDialog extends ComfyDialog { - constructor(listData, typeData) { - super(); - const __this = this - let modelListData = listData - let typeListData = typeData - this.typeListData = typeListData - - const close_button = $el("button.comfy-bizyair-close", { - type: "button", - textContent: "Close", - onclick: () => this.remove() - }); - const submit_button = $el("button.comfy-bizyair-submit", { - type: "button", - textContent: "Submit", - style: { display: 'none' }, - id: 'bizyair-upload-submit', - onclick: () => initUpload.toSubmit() - }); - const handleTabItemClass = (ele) => { - const tabItem = document.querySelectorAll('.bizyair-header-tab-item'); - tabItem.forEach(e => { - e.className = 'bizyair-header-tab-item' - }) - ele.className = 'bizyair-header-tab-item bizyair-header-tab-item-active' - } - const initUpload = uploadPage(typeListData, submit_button) - this.initUpload = initUpload - const content = - $el("div.comfy-modal-content", - [ - $el("div.bizyair-header-tab", {}, [ - $el('div.bizyair-header-tab-item.bizyair-header-tab-item-active', { - onclick: function() { - __this.showModel() - handleTabItemClass(this) - document.querySelector('#bizyair-upload-submit').style.display = 'none' - } - }, ['Remote Folders']), - $el('div.bizyair-header-tab-item', { - onclick: function() { - __this.showUpload() - handleTabItemClass(this) - document.querySelector('#bizyair-upload-submit').style.display = 'block' - } - }, ['Upload']) - ]), - $el('div.bizyair-d-content-item', { - id: 'bizyair-d-model', - style: { display: 'block' } - }, [ modelList(modelListData, typeListData) ]), - $el('div.bizyair-d-content-item', { - id: 'bizyair-d-upload', - style: { display: 'none' } - }, [ - initUpload.content - ]), - $el('div.cm-bottom-footer', {}, [submit_button, close_button]), - ] - ); - this.element = $el('div.bizyair-modal', { - parent: document.body, - }, [ - $el("div.comfy-modal.bizyair-dialog", { - id: 'bizyair-model-dialog', - parent: document.body, - style: { display: 'block' } - }, [content]) - ]) - document.addEventListener('keydown', (e) => this.keyDown(e)); - } - showModel() { - models_files('bizyair/lora').then(res => { - document.querySelector('#bizyair-d-model').style.display = 'block' - document.querySelector('#bizyair-d-upload').style.display = 'none' - document.querySelector('#bizyair-d-model').innerHTML = '' - document.querySelector('#bizyair-d-model').appendChild(modelList(res.data, this.typeListData)) - }) - } - showUpload() { - document.querySelector('#bizyair-d-model').style.display = 'none' - document.querySelector('#bizyair-d-upload').style.display = 'block' - this.initUpload.redraw() - } - keyDown(e) { - if (e.key === 'Escape') { - this.remove(); - } - } - remove() { - this.element.remove() - document.removeEventListener('keydown', (e) => this.keyDown(e)); - } - showDialog(listData, typeData) { - this.element.style.display = "block"; - this.modelListData = listData - this.typeListData = typeData - } -} diff --git a/js/dialog/modelList.js b/js/dialog/modelList.js index ee4505c9..d2f79a90 100644 --- a/js/dialog/modelList.js +++ b/js/dialog/modelList.js @@ -1,8 +1,14 @@ +import { dialog } from '../subassembly/dialog.js'; import { $el } from "../../../scripts/ui.js"; -import { ConfirmDialog } from "../subassembly/confirm.js"; -import { delModels, models_files } from "../apis.js" +import { delModels, models_files, model_types } from "../apis.js" +import { subscribe, unsubscribe } from '../subassembly/subscribers.js' -export const modelList = (listData, typeList) => { +export const modelList = async () => { + + const resList = await models_files('bizyair/lora'); + const resType = await model_types(); + const listData = resList.data; + const typeList = resType.data; const elDataItemChild = (list) => { return list.map(item => $el('div.bizyair-model-list-item-child.bizyair-model-list-item', {}, [ $el('div.bizyair-flex-item', { title: item.label_path}, [item.label_path]), @@ -12,9 +18,9 @@ export const modelList = (listData, typeList) => { ])) } const del = (name, ele) => { - new ConfirmDialog({ + dialog({ title: "This operation cannot be undone.", - message: "Are you sure you want to delete it?", + content: "Are you sure you want to delete it?", yesText: "Yes", noText: "No", onYes: () => { @@ -26,6 +32,7 @@ export const modelList = (listData, typeList) => { ele.closest('.bizyair-model-list-item').remove() } }) + return true } }) } @@ -70,8 +77,7 @@ export const modelList = (listData, typeList) => { }) } - - return $el('div.bizyair-model-list', {}, [ + const content = $el('div.bizyair-model-list', {}, [ $el('div.bizyair-model-filter-item', {}, [ $el("span.bizyair-filter-label", {}, ['Filter']), $el("select.cm-input-item", { @@ -90,5 +96,28 @@ export const modelList = (listData, typeList) => { { id: 'bizyair-model-list-item-body' }, elDataItem(listData) ) - ]) -}; + ]); + const fnMessage = (data) => { + const res = JSON.parse(data.data); + if (res && res.type == "synced") { + const elItemBody = document.querySelector('#bizyair-model-list-item-body') + models_files(document.getElementById('bizyair-model-filter').value).then(res => { + if (res.code == 20000) { + elItemBody.innerHTML = '' + const elData = elDataItem(res.data) + elData.length && elData.forEach(ele => { + elItemBody.appendChild(ele) + }); + } + }) + } + } + subscribe('socketMessage', fnMessage); + dialog({ + content: content, + noText: 'Close', + onNo: () => { + unsubscribe('socketMessage', fnMessage) + } + }) +} diff --git a/js/dialog/uploadFile.js b/js/dialog/uploadFile.js index 735d12d1..36d4b5ac 100644 --- a/js/dialog/uploadFile.js +++ b/js/dialog/uploadFile.js @@ -1,9 +1,12 @@ import { app } from "../../../scripts/app.js"; import { $el } from "../../../scripts/ui.js"; import { ConfirmDialog } from "../subassembly/confirm.js"; -import { check_model_exists, model_upload, file_upload } from "../apis.js" +import { check_model_exists, model_upload, file_upload, model_types } from "../apis.js" +import { dialog } from '../subassembly/dialog.js'; -export function uploadPage (typeList, submitBtn) { +export const uploadPage = async () => { + const resType = await model_types(); + const typeList = resType.data; const elOptions = typeList.map(item => $el("option", { value: item.value }, [item.label])) const temp = { filesAry: [], @@ -118,8 +121,7 @@ export function uploadPage (typeList, submitBtn) { this.todoUpload() }, onNo: () => { - submitBtn.disabled = false - submitBtn.innerText = 'Submit' + this.unDisabledSubmit() document.querySelectorAll('.spinner-container').forEach(e => { e.innerHTML = '' }) @@ -160,8 +162,7 @@ export function uploadPage (typeList, submitBtn) { bizyairInputFileBox.className = `${bizyairInputFileBox.className} cm-input-item-error` return } - submitBtn.disabled = true - submitBtn.innerText = 'Waiting...' + this.disabledSubmit() this.signs = [] this.queryExists() }, @@ -175,8 +176,8 @@ export function uploadPage (typeList, submitBtn) { document.querySelector('select.cm-input-item').disabled = false document.querySelector('input.bizyair-input-file-modle').disabled = false - submitBtn.disabled = false - submitBtn.innerText = 'Submit' + document.querySelector('#bizyair-upload-submit').disabled = false + document.querySelector('#bizyair-upload-submit').innerText = 'Submit' }, todoUpload() { this.disabledInput() @@ -212,8 +213,7 @@ export function uploadPage (typeList, submitBtn) { overwrite: true, files: this.signs }).then(data => { - console.log(data) - submitBtn.style.display = 'none' + document.querySelector('#bizyair-upload-submit').style.display = 'none' this.unDisabledInput() document.querySelector('#tips-in-upload').style.display = 'none' new ConfirmDialog({ @@ -227,15 +227,12 @@ export function uploadPage (typeList, submitBtn) { formData.append('filename', file.webkitRelativePath); formData.append("upload_id", this.uploadId); file_upload(formData).then(data => { - console.log('Request successful', data); - if (fn) { fn(data) } }) .catch(error => { this.unDisabledInput() - console.log(this.unDisabledInput) console.error('Error during AJAX request', error); }); }, @@ -278,13 +275,27 @@ export function uploadPage (typeList, submitBtn) { ) }) }, + disabledSubmit() { + document.querySelector('#bizyair-upload-submit').disabled = true + document.querySelector('#bizyair-upload-submit').innerText = 'Waiting...' + }, + unDisabledSubmit() { + document.querySelector('#bizyair-upload-submit').disabled = false + document.querySelector('#bizyair-upload-submit').innerText = 'Submit' + }, redraw() { document.querySelector('#bizyair-model-name').value = '' document.querySelector('.bizyair-file-list').innerHTML = '' document.querySelector('.bizyair-input-file-modle').value = '' - submitBtn.disabled = false - submitBtn.innerText = 'Submit' } } - return temp + dialog({ + content: temp.content, + yesText: 'Submit', + yesId: 'bizyair-upload-submit', + noText: 'Close', + onYes: () => { + temp.toSubmit() + }, + }) } diff --git a/js/dialog/uploadWithInputPage.js b/js/dialog/uploadWithInputPage.js new file mode 100644 index 00000000..dd024269 --- /dev/null +++ b/js/dialog/uploadWithInputPage.js @@ -0,0 +1,266 @@ +import { $el } from "../../../scripts/ui.js"; +import { check_model_exists, model_types, check_folder, submit_upload } from "../apis.js" +import { dialog } from '../subassembly/dialog.js'; +import { subscribe, unsubscribe } from '../subassembly/subscribers.js' + +export const uploadWithInputPage = async () => { + const Q = (selector) => document.querySelector(selector); + const QAll = (selector) => document.querySelectorAll(selector); + const resType = await model_types(); + const typeList = resType.data; + const elOptions = typeList.map(item => $el("option", { value: item.value }, [item.label])) + const temp = { + filesAry: [], + content: $el("div.comfy-modal-content.comfy-modal-content-file", [ + $el("div.bizyair-form-item", {}, [ + $el("span.bizyair-form-label", {}, ['Type']), + $el("select.cm-input-item", { + onchange: function () { + this.className = this.className.replace(/cm-input-item-error/g, '') + } + }, [ + ...elOptions + ]), + $el("i.bizyair-form-qa", { + onmouseover: function () { + temp.showQA(this, 'Model types.') + }, + onmouseout: function () { + temp.hideQA(this) + } + }, ['?']), + ]), + $el("div.bizyair-form-item", {}, [ + $el("span.bizyair-form-label", {}, ['Name']), + $el("input.cm-input-item", { + type: "text", + placeholder: "The remote folder name", + id: 'bizyair-model-name', + onchange: function () { + this.className = this.className.replace(/cm-input-item-error/g, '') + } + }), + $el("i.bizyair-form-qa", { + onmouseover: function () { + temp.showQA(this, 'Remote folder name of the model') + }, + onmouseout: function () { + temp.hideQA(this) + } + }, ['?']), + ]), + $el("div.bizyair-form-item", {}, [ + $el("span.bizyair-form-label", {}, ['Local Path']), + $el("input.cm-input-item", { + type: "text", + placeholder: "Please enter the local file path.", + id: 'bizyair-input-file-box', + onchange: function (e) { + this.className = this.className.replace(/cm-input-item-error/g, ''); + temp.onFileMultiChange(e) + } + }), + $el("i.bizyair-form-qa", { + onmouseover: function () { + temp.showQA(this, 'Please specify the ABSOLUTE PATH of the directory to be uploaded.') + }, + onmouseout: function () { + temp.hideQA(this) + } + }, ['?']), + ]), + $el("br", {}, []), + $el('ul.bizyair-file-list', {}, []), + $el("p.tips-in-upload", { + id: 'tips-in-upload', + style: { display: 'none' } + }, ["Please do not close this dialog box or perform any other operations while the file is uploading."]), + ] + ), + showQA(ele, text) { + $el('span.bizyair-form-qa-hint', { + parent: ele, + }, [text]) + }, + hideQA(ele) { + ele.querySelector('.bizyair-form-qa-hint').remove() + }, + queryExists() { + const type = Q('select.cm-input-item').value + const name = Q('input.cm-input-item').value + check_model_exists(type, name).then(data => { + if (data.code === 20000) { + if (data.data.exists) { + this.confirmExists() + } else { + QAll('.spinner-container').forEach(e => { + e.innerHTML = `` + }) + this.todoUpload() + } + } + + }); + }, + confirmExists() { + dialog({ + title: "The model already exists", + content: "Do you want to overwrite it?", + yesText: "Yes", + noText: "No", + onYes: () => { + QAll('.spinner-container').forEach(e => { + e.innerHTML = `` + }) + this.todoUpload(); + return true + }, + onNo: () => { + this.unDisabledInput() + QAll('.spinner-container').forEach(e => { + e.innerHTML = '' + }) + } + }) + }, + toSubmit() { + const elSelect = Q('select.cm-input-item') + const elInput = Q('input.cm-input-item') + const cmFileList = Q('.bizyair-file-list') + const bizyairInputFileBox = Q('#bizyair-input-file-box') + + if (!elSelect.value) { + dialog({ + tyoe: 'warning', + content: "Please select model type", + noText: 'Close' + }) + elSelect.className = `${elSelect.className} cm-input-item-error` + return + + } + if (!elInput.value) { + dialog({ + type: 'warning', + content: "Please input model name", + noText: 'Close' + }) + elInput.className = `${elInput.className} cm-input-item-error` + return + } + if (/^[A-Za-z0-9\u4e00-\u9fa5]([A-Za-z0-9\u4e00-\u9fa5-_]*)$/.test(elInput.value) == false) { + dialog({ + type: 'warning', + content: "Please enter English letters, Chinese characters, numbers, or - or _.", + noText: 'Close' + }) + elInput.className = `${elInput.className} cm-input-item-error` + return + } + if (cmFileList.querySelectorAll('li').length == 0) { + dialog({ + type: 'warning', + content: "Please select files", + noText: 'Close' + }) + bizyairInputFileBox.className = `${bizyairInputFileBox.className} cm-input-item-error` + return + } + this.queryExists() + + }, + disabledInput() { + Q('input.cm-input-item').disabled = true + Q('select.cm-input-item').disabled = true + Q('#bizyair-input-file-box').disabled = true + + Q('#bizyair-upload-submit').style.display = 'none' + Q('#bizyair-upload-reset').style.display = 'none' + }, + unDisabledInput() { + Q('input.cm-input-item').disabled = false + Q('select.cm-input-item').disabled = false + Q('#bizyair-input-file-box').disabled = false + + Q('#bizyair-upload-reset').style.display = 'block' + }, + todoUpload() { + const elSelect = Q('select.cm-input-item') + const elInput = Q('input.cm-input-item') + + submit_upload({ + upload_id: this.uploadId, + name: elInput.value, + type: elSelect.value, + overwrite: true + }); + Q('#tips-in-upload').style.display = 'block' + this.disabledInput() + }, + onFileMultiChange(e) { + Q('.bizyair-file-list').innerHTML = '' + check_folder(e.target.value).then(data => { + this.filesAry = data.data.files + this.uploadId = data.data.upload_id + data.data.files.forEach(file => { + Q('.bizyair-file-list').appendChild( + $el('li', {}, [ + $el("span", {}, [`${file.path}`]), + $el("span.spinner-container", {}, []), + ]) + ) + }) + }) + }, + redraw() { + Q('#bizyair-model-name').value = '' + Q('.bizyair-file-list').innerHTML = '' + Q('#bizyair-input-file-box').value = '' + } + } + const fnMessage = (data) => { + const res = JSON.parse(data.data); + if (res.type == "progress") { + const cmFileList = QAll('.bizyair-file-list li'); + const index = temp.filesAry.map(e => e.path).indexOf(res.data.path) + if (index !== -1) { + cmFileList[index].querySelector('.spinner-container').innerHTML = `${res.data.progress}`; + Q('.bizyair-file-list').scrollTop = cmFileList[index].offsetTop - 134; + } + } + if (res.type == "status") { + if (res.data.status == "finish") { + Q('#bizyair-upload-submit').style.display = 'none' + temp.unDisabledInput() + Q('#tips-in-upload').style.display = 'none' + dialog({ + type: 'succeed', + content: "The model has been uploaded successfully.", + noText: 'Close' + }); + + } + } + }; + subscribe('socketMessage', fnMessage); + + dialog({ + content: temp.content, + yesText: 'Submit', + yesId: 'bizyair-upload-submit', + neutralId: 'bizyair-upload-reset', + noText: 'Close', + neutralText: 'Reset', + onYes: () => { + temp.toSubmit() + }, + onNeutral: () => { + Q('#bizyair-upload-submit').style.display = 'block'; + temp.redraw() + }, + onNo: () => { + unsubscribe('socketMessage', fnMessage) + temp.redraw() + } + }) +} diff --git a/js/itemButton/btnApiKey.js b/js/itemButton/btnApiKey.js index 2fbc3fb5..27cf6675 100644 --- a/js/itemButton/btnApiKey.js +++ b/js/itemButton/btnApiKey.js @@ -1,10 +1,9 @@ import { $el } from "../../../scripts/ui.js"; -import { ApiKey } from "../dialog/apiKey.js"; +import { apiKey } from "../dialog/apiKey.js"; export const apiKeyBtn = $el('div.menus-item.menus-item-key', { onclick: () => showModel(), }, ['API Key']) function showModel() { - const key = new ApiKey() - key.showDialog() + apiKey() } diff --git a/js/itemButton/btnExample.js b/js/itemButton/btnExample.js index bea1537a..4fe8eebd 100644 --- a/js/itemButton/btnExample.js +++ b/js/itemButton/btnExample.js @@ -1,82 +1,13 @@ import { api } from "../../../scripts/api.js"; -import { app } from "../../../scripts/app.js"; import { $el } from "../../../scripts/ui.js"; +import { showMenu } from "../subassembly/btnMenuFn.js"; -export const exampleBtn = $el('div.menus-item.menus-item-example', { - onclick: (e) => showMenu(e), -}, ['Examples', $el('span.menus-item-arrow', {}, [])]) let show_cases = null; -let dragging = false; -document.addEventListener("click", () => { - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); -}); - -async function showMenu(e) { - if (dragging) return; // Prevent showing menu during drag - e.preventDefault(); - e.stopPropagation(); - - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); - const keys = Object.keys(show_cases) - document.body.appendChild($el( - "ul.example-menu", - { - style: { - position: "absolute", - top: e.clientY + "px", - left: e.clientX + "px", - zIndex: 1000, - } - }, - keys.map(item => mapHtmls(show_cases[item], item)) - )) -} - -function mapHtmls(item, key) { - if (typeof item === 'string') { - return $el("li", { - textContent: key, - onclick: async () => await get_workflow_graph(item) - }); - } else { - const keys = Object.keys(item) - return $el("li.has-child", { - textContent: key, - onclick: function (e) { - e.preventDefault(); - e.stopPropagation(); - const childList = this.querySelector(".child-list"); - this.classList.toggle('show-child'); - childList.style.display = childList.style.display === 'block' ? 'none' : 'block'; - } - }, [ - $el("ul.child-list", { style: { display: "none" } }, keys.map(e => mapHtmls(item[e], e))) - ]) - } -} - -async function get_workflow_graph(file) { - if (file.startsWith("https://")) { - console.log("open BizyAir NEWS:", file); - window.open(file, '_blank'); - } else if (file.endsWith(".json")) { - console.log("workflow file:", file); - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); - const response = await api.fetchApi("/bizyair/workflow", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ file: file }), - }); - const showcase_graph = await response.json() - app.graph.clear() - await app.loadGraphData(showcase_graph) - } -} api.fetchApi("/bizyair/showcases", { method: "GET" }).then(response => response.json()).then(data => { show_cases = data }); + + +export const exampleBtn = $el('div.menus-item.menus-item-example', { + onclick: (e) => showMenu(e, show_cases), +}, ['Examples', $el('span.menus-item-arrow', {}, [])]) diff --git a/js/itemButton/btnModel.js b/js/itemButton/btnModel.js index 0ab666a5..117e0db4 100644 --- a/js/itemButton/btnModel.js +++ b/js/itemButton/btnModel.js @@ -1,18 +1,13 @@ import { $el } from "../../../scripts/ui.js"; -import { ModelDialog } from "../dialog/modelDialog.js"; -import { models_files, model_types } from "../apis.js"; +import { showMenu } from "../subassembly/btnMenuFn.js"; +import { modelList } from "../dialog/modelList.js"; +import { uploadWithInputPage } from "../dialog/uploadWithInputPage.js"; -export const modelBtn = $el('div.menus-item.menus-item-model', { - onclick: () => showModel(), -}, ['Model']) -function showModel() { - Promise.all([ - models_files('bizyair/lora'), - model_types() - ]).then(data => { - new ModelDialog(data[0].data, data[1].data).showDialog(data[0].data, data[1].data); - }).catch(error => { - console.log(error) - }); +const show_cases = { + "Remote Folders": modelList, + "Upload": uploadWithInputPage, } +export const modelBtn = $el('div.menus-item.menus-item-model', { + onclick: (e) => showMenu(e, show_cases), +}, ['Model']) diff --git a/js/itemButton/btnNews.js b/js/itemButton/btnNews.js index d31665fe..5c26a82a 100644 --- a/js/itemButton/btnNews.js +++ b/js/itemButton/btnNews.js @@ -1,82 +1,12 @@ import { api } from "../../../scripts/api.js"; -import { app } from "../../../scripts/app.js"; import { $el } from "../../../scripts/ui.js"; +import { showMenu } from "../subassembly/btnMenuFn.js"; -export const newsBtn = $el('div.menus-item.menus-item-news', { - onclick: (e) => showMenu(e), -}, ['News', $el('span.menus-item-arrow', {}, [])]) let show_cases = null; -let dragging = false; -document.addEventListener("click", () => { - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); -}); - -async function showMenu(e) { - if (dragging) return; // Prevent showing menu during drag - e.preventDefault(); - e.stopPropagation(); - - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); - const keys = Object.keys(show_cases) - document.body.appendChild($el( - "ul.example-menu", - { - style: { - position: "absolute", - top: e.clientY + "px", - left: e.clientX + "px", - zIndex: 1000, - } - }, - keys.map(item => mapHtmls(show_cases[item], item)) - )) -} - -function mapHtmls(item, key) { - if (typeof item === 'string') { - return $el("li", { - textContent: key, - onclick: async () => await get_workflow_graph(item) - }); - } else { - const keys = Object.keys(item) - return $el("li.has-child", { - textContent: key, - onclick: function (e) { - e.preventDefault(); - e.stopPropagation(); - const childList = this.querySelector(".child-list"); - this.classList.toggle('show-child'); - childList.style.display = childList.style.display === 'block' ? 'none' : 'block'; - } - }, [ - $el("ul.child-list", { style: { display: "none" } }, keys.map(e => mapHtmls(item[e], e))) - ]) - } -} - -async function get_workflow_graph(file) { - if (file.startsWith("https://")) { - console.log("open BizyAir NEWS:", file); - window.open(file, '_blank'); - } else if (file.endsWith(".json")) { - console.log("workflow file:", file); - const exampleMenu = document.querySelector(".example-menu") - if (exampleMenu) document.body.removeChild(exampleMenu); - const response = await api.fetchApi("/bizyair/workflow", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ file: file }), - }); - const showcase_graph = await response.json() - app.graph.clear() - await app.loadGraphData(showcase_graph) - } -} api.fetchApi("/bizyair/news", { method: "GET" }).then(response => response.json()).then(data => { show_cases = data }); + +export const newsBtn = $el('div.menus-item.menus-item-news', { + onclick: (e) => showMenu(e, show_cases), +}, ['News', $el('span.menus-item-arrow', {}, [])]) diff --git a/js/menus.js b/js/menus.js index 4d613851..4ac04088 100644 --- a/js/menus.js +++ b/js/menus.js @@ -7,7 +7,10 @@ import { newsBtn } from "./itemButton/btnNews.js"; import { styleExample } from "./subassembly/styleExample.js"; import { styleMenus } from "./subassembly/styleMenus.js"; import { styleUploadFile } from "./subassembly/styleUploadFile.js"; - +import { styleDialog } from './subassembly/styleDialog.js'; +import { notifySubscribers } from './subassembly/subscribers.js' +import { WebSocketClient } from './subassembly/socket.js' +import { toast } from './subassembly/toast.js' class FloatingButton { constructor(show_cases) { @@ -72,7 +75,6 @@ class FloatingButton { const comfyFloatingButton = document.querySelector('.comfy-floating-button') const bizyairMenu = document.querySelector('.bizyair-menu') const bizyairMenuCloser = document.querySelector('.cmfy-floating-button-closer') - console.log(bizyairMenu) if (this.visible) { comfyFloatingButton.className = 'comfy-floating-button comfy-floating-button-hidden'; bizyairMenu.className = 'bizyair-menu bizyair-menu-hidden'; @@ -101,6 +103,19 @@ app.registerExtension({ textContent: styleUploadFile, parent: document.head, }); + $el("style", { + textContent: styleDialog, + parent: document.head, + }); new FloatingButton(); + + const wsClient = new WebSocketClient(`ws://${location.host}/bizyair/modelhost/ws?clientId=${sessionStorage.getItem('clientId')}`); + wsClient.onMessage = function(message) { + notifySubscribers('socketMessage', message); + const res = JSON.parse(message.data); + if (res && res.type == 'errors') { + toast.error(res.data.message) + } + } }, }); diff --git a/js/subassembly/btnMenuFn.js b/js/subassembly/btnMenuFn.js new file mode 100644 index 00000000..6edd933a --- /dev/null +++ b/js/subassembly/btnMenuFn.js @@ -0,0 +1,79 @@ +import { app } from "../../../scripts/app.js"; +import { $el } from "../../../scripts/ui.js"; +import { api } from "../../../scripts/api.js"; + +let dragging = false; +document.addEventListener("click", () => { + const exampleMenu = document.querySelector(".example-menu") + if (exampleMenu) document.body.removeChild(exampleMenu); +}); + +export async function showMenu(e, show_cases) { + if (dragging) return; + e.preventDefault(); + e.stopPropagation(); + + const exampleMenu = document.querySelector(".example-menu") + if (exampleMenu) document.body.removeChild(exampleMenu); + const keys = Object.keys(show_cases) + document.body.appendChild($el( + "ul.example-menu", + { + style: { + position: "absolute", + top: e.clientY + "px", + left: e.clientX + "px", + zIndex: 1000, + } + }, + keys.map(item => mapHtmls(show_cases[item], item)) + )) +} + +export function mapHtmls(item, key) { + if (typeof item === 'string' || typeof item === 'function') { + return $el("li", { + textContent: key, + onclick: async () => await get_workflow_graph(item) + }); + } else { + const keys = Object.keys(item) + return $el("li.has-child", { + textContent: key, + onclick: function (e) { + e.preventDefault(); + e.stopPropagation(); + const childList = this.querySelector(".child-list"); + this.classList.toggle('show-child'); + childList.style.display = childList.style.display === 'block' ? 'none' : 'block'; + } + }, [ + $el("ul.child-list", { style: { display: "none" } }, keys.map(e => mapHtmls(item[e], e))) + ]) + } +} + +export async function get_workflow_graph(file) { + if (typeof file === 'function') { + file() + return + } + if (file.startsWith("https://")) { + console.log("open BizyAir NEWS:", file); + window.open(file, '_blank'); + } else if (file.endsWith(".json")) { + console.log("workflow file:", file); + const exampleMenu = document.querySelector(".example-menu") + if (exampleMenu) document.body.removeChild(exampleMenu); + const response = await api.fetchApi("/bizyair/workflow", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ file: file }), + }); + const showcase_graph = await response.json() + app.graph.clear() + await app.loadGraphData(showcase_graph) + } +} diff --git a/js/subassembly/dialog.js b/js/subassembly/dialog.js new file mode 100644 index 00000000..ab0d6509 --- /dev/null +++ b/js/subassembly/dialog.js @@ -0,0 +1,149 @@ +import { $el } from "../../../scripts/ui.js"; + + +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = window.crypto.getRandomValues(new Uint8Array(1))[0] % 16 | (c === 'x' ? 0 : 8); + return r.toString(16); + }); +} +let dialogStack = []; +export function dialog(params) { + const id = 'bizyair-dialog' + generateUUID(); + const style = {} + let h = 'calc(80vh - 40px - 40px)'; + if (params.yesText || params.noText || params.neutralText) { + h = 'calc(80vh - 40px - 40px - 34px)' + } + if (params.title) { + h = 'calc(80vh - 40px - 40px - 34px - 48px)' + } + function setContent() { + if (params.content) { + return $el("div.bizyair-new-dialog-body", { + style: { maxHeight: h } + }, [ + (params.type && params.type == 'succeed'? $el('div.bizyair-new-dialog-icon.bizyair-new-dialog-succeed', {}, []): ''), + (params.type && params.type == 'warning'? $el('div.bizyair-new-dialog-icon.bizyair-new-dialog-warning', {}, []): ''), + (params.type && params.type == 'error'? $el('div.bizyair-new-dialog-icon.bizyair-new-dialog-error', {}, []): ''), + params.content + ]) + } + return '' + } + const el = $el("div.bizyair-new-dialog", { + parent: document.body, + id, + style: { zIndex: 10000 + document.querySelectorAll('.bizyair-new-dialog').length }, + onclick: function () { + if (params.closeOnClickModal) { + removeDialog(this) + } + }, + }, [ + $el("div.bizyair-dialog-content", { + style, + onclick: function (e) { + e.stopPropagation(); + } + }, [ + (params.title ? $el("p.bizyair-new-dialog-title", { }, [params.title]) : ''), + setContent(), + $el('div.bizyair-new-dialog-footer', {}, [ + (params.yesText ? $el("button.bizyair-new-dialog-btn", { + type: "button", + textContent: params.yesText, + id: params.yesId ? params.yesId : '', + onclick: async () => { + if (params.onYes) { + const res = await params.onYes(); + if (!res) { + return false + } + } + removeDialog(document.getElementById(id)) + } + }): ''), + (params.neutralText ? $el("button.bizyair-new-dialog-btn", { + type: "button", + textContent: params.neutralText, + id: params.neutralId ? params.neutralId : '', + onclick: async () => { + if (params.onNeutral) { + const res = await params.onNeutral(); + if (!res) { + return false + } + } + removeDialog(document.getElementById(id)) + } + }): ''), + (params.noText ? $el("button.bizyair-new-dialog-btn", { + type: "button", + textContent: params.noText, + onclick: async () => { + if (params.onNo) { + await params.onNo(); + } + removeDialog(document.getElementById(id)) + } + }): '') + ]), + ]) + ]); + const fnEscapeClose = async function (e) { + if (e.key === "Escape") { + const topDialog = dialogStack[dialogStack.length - 1]; + + if (topDialog === el) { + if (params.onNo) { + await params.onNo(); + } + removeDialog(el); + } + } + } + dialogStack.push(el); + + if (!params.onEscape) { + document.addEventListener("keydown", fnEscapeClose); + } + + function removeDialog(el) { + requestAnimationFrame(() => { + el.querySelector('.bizyair-dialog-content').style.transition = 'all 0.2s'; + el.querySelector('.bizyair-dialog-content').style.transform = 'translate(-50%, -50%) scale(0)'; + el.style.transition = 'all 0.3s'; + el.style.opacity = '0'; + setTimeout(() => { + el.remove(); + }, 200); + }); + document.removeEventListener("keydown", fnEscapeClose); + dialogStack = dialogStack.filter(d => d !== el); + } +} +dialog.succeed = params => { + if (typeof params === 'string') { + const contentParams = { content: params, type: 'succeed' } + dialog(contentParams) + } + params.type = 'succeed'; + dialog(params) +} +dialog.warning = params => { + if (typeof params === 'string') { + const contentParams = { content: params, type: 'warning' } + dialog(contentParams) + } + params.type = 'warning'; + dialog(params) +} +dialog.error = params => { + if (typeof params === 'string') { + const contentParams = { content: params, type: 'error' } + dialog(contentParams) + } + params.type = 'error'; + dialog(params) +} diff --git a/js/subassembly/socket.js b/js/subassembly/socket.js new file mode 100644 index 00000000..5047b65c --- /dev/null +++ b/js/subassembly/socket.js @@ -0,0 +1,107 @@ +export class WebSocketClient { + constructor(url, protocols) { + this.url = url; + this.protocols = protocols; + this.reconnectDelay = 1000; + this.maxReconnectDelay = 30000; + this.keepAliveInterval = 10000; + this.ws = null; + this.keepAliveTimer = null; + this.reconnectTimer = null; + + this.connect(); + } + + connect() { + this.ws = new WebSocket(this.url, this.protocols); + this.ws.onopen = () => { + this.onOpen(); + }; + + this.ws.onmessage = (message) => { + if (message.data !== 'pong') { + this.onMessage(message); + }; + }; + + + this.ws.onerror = (error) => { + this.onError(error); + }; + + + this.ws.onclose = () => { + console.warn('WebSocket 连接已关闭,准备重连'); + this.onClose(); + this.scheduleReconnect(); + }; + } + + + startKeepAlive() { + if (this.keepAliveTimer) return; + + this.keepAliveTimer = setInterval(() => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.send('ping'); + } + }, this.keepAliveInterval); + } + + stopKeepAlive() { + if (this.keepAliveTimer) { + clearInterval(this.keepAliveTimer); + this.keepAliveTimer = null; + } + } + + + scheduleReconnect() { + if (this.reconnectTimer) return; + + this.reconnectTimer = setTimeout(() => { + console.log(`尝试重新连接...`); + this.connect(); + this.reconnectTimer = null; + + this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay); + }, this.reconnectDelay); + } + + onOpen() { + + this.reconnectDelay = 2000; + this.startKeepAlive(); + } + + onMessage(message) { + + const data = JSON.parse(message.data); + if (data === 'pong') { + } else { + console.log('message:', data); + } + } + + onError(error) { + + console.error('WebSocket 错误: ', error); + } + + onClose() { + + this.stopKeepAlive(); + } + + + close() { + if (this.ws) { + this.ws.close(); + } + this.stopKeepAlive(); + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + } +} diff --git a/js/subassembly/styleDialog.js b/js/subassembly/styleDialog.js new file mode 100644 index 00000000..c5ca70d6 --- /dev/null +++ b/js/subassembly/styleDialog.js @@ -0,0 +1,140 @@ +export const styleDialog = ` +.bizyair-new-dialog{ + position: fixed; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.6); +} +.bizyair-dialog-content{ + position: fixed; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(1); + padding: 20px; + min-width: 400px; + min-height: 120px; + max-height: 80vh; + max-width: 80vw; + border-radius: var(--p-dialog-border-radius); + box-shadow: var(--p-dialog-shadow); + background: var(--p-dialog-background); + border: 1px solid var(--p-dialog-border-color); + color: var(--p-dialog-color); +} +.bizyair-new-dialog-title{ + font-size: 20px; + line-height: 48px; + font-weight: bold; + margin: 0; + padding: 0; +} +.bizyair-new-dialog-body{ + margin: 0 0 20px 0; + overflow-y: auto; +} +.bizyair-new-dialog-footer{ + display: flex; + justify-content: flex-end; +} +.bizyair-new-dialog-btn{ + color: var(--input-text); + background-color: var(--comfy-input-bg); + border-radius: 8px; + border-color: var(--border-color); + border-style: solid; + fons-size: 20px; + box-sizing: border-box; + line-height: 30px; + cursor: pointer; + padding: 0 18px; + margin-left: 10px; +} +.bizyair-new-dialog-btn:hover{ + background-color: var(--comfy-input-bg-hover); +} +.bizyair-new-dialog-icon{ + width: 64px; + height: 64px; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + margin: 20px auto; +} +.bizyair-new-dialog-succeed{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 36 36'%3E%3Cpath fill='%2367c23a' d='M18 2a16 16 0 1 0 16 16A16 16 0 0 0 18 2m10.45 10.63L15.31 25.76L7.55 18a1.4 1.4 0 0 1 2-2l5.78 5.78l11.14-11.13a1.4 1.4 0 1 1 2 2Z' class='clr-i-solid clr-i-solid-path-1'/%3E%3Cpath fill='none' d='M0 0h36v36H0z'/%3E%3C/svg%3E"); +} +.bizyair-new-dialog-warning{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 20 20'%3E%3Cpath fill='%23e6a23c' d='M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8s-8-3.58-8-8s3.58-8 8-8m1.13 9.38l.35-6.46H8.52l.35 6.46zm-.09 3.36c.24-.23.37-.55.37-.96c0-.42-.12-.74-.36-.97s-.59-.35-1.06-.35s-.82.12-1.07.35s-.37.55-.37.97c0 .41.13.73.38.96c.26.23.61.34 1.06.34s.8-.11 1.05-.34'/%3E%3C/svg%3E"); +} +.bizyair-new-dialog-error{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23f56c6c' d='M12 4a8 8 0 1 0 0 16a8 8 0 0 0 0-16M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m5.793-4.207a1 1 0 0 1 1.414 0L12 10.586l2.793-2.793a1 1 0 1 1 1.414 1.414L13.414 12l2.793 2.793a1 1 0 0 1-1.414 1.414L12 13.414l-2.793 2.793a1 1 0 0 1-1.414-1.414L10.586 12L7.793 9.207a1 1 0 0 1 0-1.414'/%3E%3C/svg%3E"); +} +.bizyair-toast{ + position: fixed; + right: 0; + padding: 10px 0; + border-radius: 8px 0 0 8px; + z-index: 10000; + display: flex; + align-items: center; + min-width: 240px; + border: 1px solid #000; + height: 40px; + line-height: 40px; +} +.bizyair-toast-succeed{ + border-color: #67c23a; + color: #67c23a; + background-color: rgba(103, 192, 58, 0.2); +} +.bizyair-toast-warning{ + border-color: #dba44b; + color: #dba44b; + background-color: rgba(219, 164, 75, 0.2); +} +.bizyair-toast-error{ + border-color: #f56c6c; + color: #f56c6c; + background-color: rgba(245, 108, 108, 0.2); +} +.bizyair-toast-icon{ + width: 20px; + height: 20px; + margin: 0 10px; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 36 36'%3E%3Cpath fill='%2367c23a' d='M18 2a16 16 0 1 0 16 16A16 16 0 0 0 18 2m10.45 10.63L15.31 25.76L7.55 18a1.4 1.4 0 0 1 2-2l5.78 5.78l11.14-11.13a1.4 1.4 0 1 1 2 2Z' class='clr-i-solid clr-i-solid-path-1'/%3E%3Cpath fill='none' d='M0 0h36v36H0z'/%3E%3C/svg%3E"); + background-size: 20px 20px; +} +.bizyair-toast-icon-warning{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23dba44b' d='M13 13h-2V7h2m0 10h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2'/%3E%3C/svg%3E") +} +.bizyair-toast-icon-error{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23f56c6c' d='M13 13h-2V7h2m0 10h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2'/%3E%3C/svg%3E"); +} +.bizyair-toast-content{ + padding-right: 40px; + flex: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.bizyair-toast-close{ + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + width: 20px; + height: 20px; + cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%2367c23a' d='M3 16.74L7.76 12L3 7.26L7.26 3L12 7.76L16.74 3L21 7.26L16.24 12L21 16.74L16.74 21L12 16.24L7.26 21zm9-3.33l4.74 4.75l1.42-1.42L13.41 12l4.75-4.74l-1.42-1.42L12 10.59L7.26 5.84L5.84 7.26L10.59 12l-4.75 4.74l1.42 1.42z'/%3E%3C/svg%3E"); + background-size: 20px 20px; + background-repeat: no-repeat; +} +.bizyair-toast-warning .bizyair-toast-close{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 48 48'%3E%3Cpath fill='none' stroke='%23dba44b' stroke-linecap='round' stroke-linejoin='round' stroke-width='4' d='m6 11l5-5l13 13L37 6l5 5l-13 13l13 13l-5 5l-13-13l-13 13l-5-5l13-13z' clip-rule='evenodd'/%3E%3C/svg%3E"); +} +.bizyair-toast-error .bizyair-toast-close{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23f56c6c' d='M3 16.74L7.76 12L3 7.26L7.26 3L12 7.76L16.74 3L21 7.26L16.24 12L21 16.74L16.74 21L12 16.24L7.26 21zm9-3.33l4.74 4.75l1.42-1.42L13.41 12l4.75-4.74l-1.42-1.42L12 10.59L7.26 5.84L5.84 7.26L10.59 12l-4.75 4.74l1.42 1.42z'/%3E%3C/svg%3E") +} +` diff --git a/js/subassembly/styleUploadFile.js b/js/subassembly/styleUploadFile.js index 6af46f97..8df6b497 100644 --- a/js/subassembly/styleUploadFile.js +++ b/js/subassembly/styleUploadFile.js @@ -10,7 +10,7 @@ export const styleUploadFile = ` z-index: 10000000; } .bizyair-dialog{ - width: 1000px; + width: 1200px; height: 520px; box-sizing: content-box; z-index: 10000; @@ -63,8 +63,14 @@ export const styleUploadFile = ` margin-top: 40px; } .comfy-modal-content{ + width: 1000px; + height: 100%; +} +.comfy-modal-content-sml{ width: 100%; height: 100%; + display: flex; + flex-direction: column; } .cm-bottom-footer { width: calc(100% - 64px); @@ -115,7 +121,7 @@ p.cm-word-file-modle{ .bizyair-file-list{ width: 100%; box-sizing: border-box; - height: 220px; + height: 200px; overflow-y: auto; padding: 10px; margin: 0; @@ -207,7 +213,7 @@ input.cm-input-item-error{ margin-left: 10px; } .bizyair-model-list{ - width: 100%; + width: 1000px; height: 360px; margin-top: 40px; color: var(--input-text); @@ -351,10 +357,12 @@ input.cm-input-item-error{ .upload-size-hint{ position: absolute; left: 104px; - bottom: -40px; + bottom: -30px; margin: 0; padding: 0; color: #e6a23c; + height: 30px; + line-height: 30px; } p.tips-in-upload{ margin: 4px 0 0 0; diff --git a/js/subassembly/subscribers.js b/js/subassembly/subscribers.js new file mode 100644 index 00000000..f74deb3b --- /dev/null +++ b/js/subassembly/subscribers.js @@ -0,0 +1,23 @@ +const subscribers = new Map(); + +export function subscribe(key, callback) { + if (!subscribers.has(key)) { + subscribers.set(key, []); + } + subscribers.get(key).push(callback); +} + +export function unsubscribe(key, callback) { + if (subscribers.has(key)) { + const index = subscribers.get(key).indexOf(callback); + if (index > -1) { + subscribers.get(key).splice(index, 1); + } + } +} + +export function notifySubscribers(key, data) { + if (subscribers.has(key)) { + subscribers.get(key).forEach(callback => callback(data)); + } +} diff --git a/js/subassembly/toast.js b/js/subassembly/toast.js new file mode 100644 index 00000000..c7529110 --- /dev/null +++ b/js/subassembly/toast.js @@ -0,0 +1,71 @@ +import { $el } from "../../../scripts/ui.js"; + +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = window.crypto.getRandomValues(new Uint8Array(1))[0] % 16 | (c === 'x' ? 0 : 8); + return r.toString(16); + }); +} +function removeToast(el) { + requestAnimationFrame(() => { + el.style.transition = 'all 0.2s'; + el.style.opacity = '0'; + el.style.transform = 'translate(50%, 0) scale(0)'; + setTimeout(() => { + el.remove(); + }, 200); + }); +} +export function toast(params) { + if (typeof params === 'string') { + params = { + content: params + }; + } + const id = 'bizyair-toast' + generateUUID(); + const style = { + top: `${document.querySelectorAll('.bizyair-toast').length * 60 + 100}px` + } + let iTime = null; + params.type = params.type || 'succeed'; + $el(`div.bizyair-toast${params.type ? `.bizyair-toast-${params.type}` : ''}`, { + id, + style, + parent: document.body, + }, [ + (params.type && params.type == 'succeed' ? $el('div.bizyair-toast-icon.bizyair-toast-icon-succeed', {}, []) : ''), + (params.type && params.type == 'warning' ? $el('div.bizyair-toast-icon.bizyair-toast-icon-warning', {}, []) : ''), + (params.type && params.type == 'error' ? $el('div.bizyair-toast-icon.bizyair-toast-icon-error', {}, []) : ''), + $el('span.bizyair-toast-content', {}, [params.content]), + $el('span.bizyair-toast-close', {}, []), + ]); + iTime = setTimeout(() => { + removeToast(document.querySelector(`#${id}`)); + clearTimeout(iTime); + }, 3000); + document.querySelector(`#${id} .bizyair-toast-close`).addEventListener('click', () => { + removeToast(document.querySelector(`#${id}`)); + clearTimeout(iTime); + }); + document.querySelector(`#${id}`).addEventListener('mouseover', () => { + clearTimeout(iTime); + }); + document.querySelector(`#${id}`).addEventListener('mouseout', () => { + iTime = setTimeout(() => { + removeToast(document.querySelector(`#${id}`)); + clearTimeout(iTime); + }, 3000); + }); +} +toast.warning = (params) => { + toast({ + content: params, + type: 'warning' + }) +} +toast.error = (params) => { + toast({ + content: params, + type: 'error' + }) +} diff --git a/requirements.txt b/requirements.txt index 508f4ac7..3f73b9b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ numpy<2.0 oss2 crcmod -requests \ No newline at end of file +requests +aiofiles \ No newline at end of file diff --git a/showcase.py b/showcase.py index 011858a8..62defdd3 100644 --- a/showcase.py +++ b/showcase.py @@ -1,3 +1,4 @@ +import asyncio import json import os import urllib.error @@ -27,8 +28,11 @@ async def get_bizyair_news(base_url="https://bizyair.siliconflow.cn"): except aiohttp.ClientError as e: print(f"Error fetching news.json: {e}") return {} + except asyncio.exceptions.TimeoutError as e: + print(f"Request bizyair news timed out: {e}") + return {} except Exception as e: - print(f"Error fetching BizyAir bznews.json: {str(e)}") + print(f"Error fetching BizyAir bznews.json: {type(e).__name__} - {str(e)}") return {} diff --git a/src/bizy_server/__init__.py b/src/bizy_server/__init__.py index 36d03beb..ee09ff0f 100644 --- a/src/bizy_server/__init__.py +++ b/src/bizy_server/__init__.py @@ -1,5 +1,13 @@ +import threading + +from .execution import upload_worker from .modelhost import ModelHostServer modelhost_server = ModelHostServer() +threading.Thread( + target=upload_worker, + daemon=True, + args=(modelhost_server, modelhost_server.upload_queue), +).start() if modelhost_server: print("\n\n\033[92m[BizyAir]\033[0m Model hosting service initialized.\n\n") diff --git a/src/bizy_server/errno.py b/src/bizy_server/errno.py index 88a11e76..ca964f17 100644 --- a/src/bizy_server/errno.py +++ b/src/bizy_server/errno.py @@ -9,11 +9,18 @@ def __init__(self, http_status_code, code, payload, message): CODE_OK = 20000 CODE_NO_MODEL_FOUND = 20226 -INVALID_TYPE = ErrorNo(400, 400100, None, "type is invalid") -INVALID_NAME = ErrorNo(400, 400101, None, "name is invalid") -NO_FILE_UPLOAD_ERR = ErrorNo(400, 400102, None, "no file uploaded") -EMPTY_UPLOAD_ID_ERR = ErrorNo(400, 400103, None, "empty upload id") -INVALID_API_KEY_ERR = ErrorNo(401, 401000, None, "invalid API key") +INVALID_TYPE = ErrorNo(400, 400100, None, "Invalid type") +INVALID_NAME = ErrorNo(400, 400101, None, "Invalid name") +NO_FILE_UPLOAD_ERR = ErrorNo(400, 400102, None, "No file to upload") +EMPTY_UPLOAD_ID_ERR = ErrorNo(400, 400103, None, "Upload id is Empty") +EMPTY_ABS_FOLDER_ERR = ErrorNo(400, 400111, None, "The upload path cannot be empty.") +NO_ABS_PATH_ERR = ErrorNo(400, 400112, None, "The upload path is not an absolute path.") +PATH_NOT_EXISTS_ERR = ErrorNo(400, 400113, None, "The upload path does not exist.") +FILE_NOT_EXISTS_ERR = ErrorNo(400, 400114, None, "The file does not exist.") +INVALID_CLIENT_ID_ERR = ErrorNo(400, 400114, None, "Invalid client id") +INVALID_API_KEY_ERR = ErrorNo(401, 401000, None, "Invalid API key") +INVALID_USER_ERR = ErrorNo(401, 401001, None, "Invalid user") + FILE_UPLOAD_SIZE_LIMIT_ERR = ErrorNo( 413, 413000, @@ -21,14 +28,16 @@ def __init__(self, http_status_code, code, payload, message): 'File size exceeds the allowed limit. Please use the "--max-upload-size" to specify the upload file size limit, default is 100MB.', ) -CHECK_MODEL_EXISTS_ERR = ErrorNo(500, 500100, None, "failed to check model") -SIGN_FILE_ERR = ErrorNo(500, 500101, None, "failed to sign file") -UPLOAD_ERR = ErrorNo(500, 500102, None, "failed to upload file") -COMMIT_FILE_ERR = ErrorNo(500, 500103, None, "failed to commit file") -MODEL_ALREADY_EXISTS_ERR = ErrorNo(500, 500104, None, "model already exists") -COMMIT_MODEL_ERR = ErrorNo(500, 500105, None, "failed to commit model") -INVALID_UPLOAD_ID_ERR = ErrorNo(500, 500106, None, "invalid upload id") -EMPTY_FILES_ERR = ErrorNo(500, 500107, None, "empty files to make a model") -LIST_MODEL_FILE_ERR = ErrorNo(500, 500108, None, "failed to list model file") -INVALID_FILENAME_ERR = ErrorNo(500, 500109, None, "invalid filename") -DELETE_MODEL_ERR = ErrorNo(500, 500110, None, "failed to delete model") +CHECK_MODEL_EXISTS_ERR = ErrorNo(500, 500100, None, "Failed to check model") +SIGN_FILE_ERR = ErrorNo(500, 500101, None, "Failed to sign file") +UPLOAD_ERR = ErrorNo(500, 500102, None, "Failed to upload file") +COMMIT_FILE_ERR = ErrorNo(500, 500103, None, "Failed to commit file") +MODEL_ALREADY_EXISTS_ERR = ErrorNo(500, 500104, None, "Model already exists") +COMMIT_MODEL_ERR = ErrorNo(500, 500105, None, "Failed to commit model") +INVALID_UPLOAD_ID_ERR = ErrorNo(500, 500106, None, "Invalid upload id") +EMPTY_FILES_ERR = ErrorNo(500, 500107, None, "Empty files to make a model") +LIST_MODEL_FILE_ERR = ErrorNo(500, 500108, None, "Failed to list model file") +INVALID_FILENAME_ERR = ErrorNo(500, 500109, None, "Invalid filename") +DELETE_MODEL_ERR = ErrorNo(500, 500110, None, "Failed to delete model") +GET_USER_INFO_ERR = ErrorNo(500, 500111, None, "Failed to get user info") +LIST_MODEL_ERR = ErrorNo(500, 500112, None, "Failed to list model") diff --git a/src/bizy_server/execution.py b/src/bizy_server/execution.py new file mode 100644 index 00000000..0d63644d --- /dev/null +++ b/src/bizy_server/execution.py @@ -0,0 +1,54 @@ +import asyncio +import copy +import heapq +import logging +import threading + + +class UploadQueue: + def __init__(self): + self.mutex = threading.RLock() + self.not_empty = threading.Condition(self.mutex) + self.task_counter = 0 + self.queue = [] + self.currently_running = {} + + def put(self, item): + with self.mutex: + heapq.heappush(self.queue, item) + self.not_empty.notify() + + def get(self, timeout=None): + with self.not_empty: + while len(self.queue) == 0: + self.not_empty.wait(timeout=timeout) + if timeout is not None and len(self.queue) == 0: + return None + item = heapq.heappop(self.queue) + i = self.task_counter + self.currently_running[i] = copy.deepcopy(item) + self.task_counter += 1 + return (item, i) + + def task_done(self, item_id): + with self.mutex: + self.currently_running.pop(item_id) + + +def upload_worker(server, q): + timeout = 1000.0 + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + while True: + try: + queue_item = q.get(timeout=timeout) + if queue_item is not None: + item, item_id = queue_item + loop = asyncio.get_event_loop() + loop.run_until_complete(server.do_upload(item)) + q.task_done(item_id) + else: + continue + except Exception as e: + logging.error(f"Failed to upload file: {e}") diff --git a/src/bizy_server/modelhost.py b/src/bizy_server/modelhost.py index f7371bbe..e406fb21 100644 --- a/src/bizy_server/modelhost.py +++ b/src/bizy_server/modelhost.py @@ -1,18 +1,23 @@ +import asyncio import base64 import hashlib import json import logging import os import shutil +import threading +import time import urllib.parse import urllib.request +import uuid from collections import defaultdict from pathlib import Path +import aiofiles +import aiohttp import crcmod import oss2 import requests -from aiohttp import web from server import PromptServer import bizyair @@ -26,23 +31,30 @@ COMMIT_FILE_ERR, COMMIT_MODEL_ERR, DELETE_MODEL_ERR, + EMPTY_ABS_FOLDER_ERR, EMPTY_FILES_ERR, EMPTY_UPLOAD_ID_ERR, + FILE_NOT_EXISTS_ERR, FILE_UPLOAD_SIZE_LIMIT_ERR, + GET_USER_INFO_ERR, INVALID_API_KEY_ERR, - INVALID_FILENAME_ERR, + INVALID_CLIENT_ID_ERR, INVALID_NAME, INVALID_TYPE, INVALID_UPLOAD_ID_ERR, + LIST_MODEL_ERR, LIST_MODEL_FILE_ERR, MODEL_ALREADY_EXISTS_ERR, + NO_ABS_PATH_ERR, NO_FILE_UPLOAD_ERR, + PATH_NOT_EXISTS_ERR, SIGN_FILE_ERR, UPLOAD_ERR, ErrorNo, ) +from .execution import UploadQueue from .oss import AliOssStorageClient -from .resp import ErrResponse, JsonResponse, OKResponse +from .resp import ErrResponse, OKResponse current_path = os.path.abspath(os.path.dirname(__file__)) prompt_server = PromptServer.instance @@ -72,14 +84,21 @@ def __init__(self): ModelHostServer.instance = self list_model_html = self.get_html_content("templates/list_model.html") upload_model_html = self.get_html_content("templates/upload_model.html") + self.sockets = dict() + self.uploads = dict() + self.upload_queue = UploadQueue() + self.loop = asyncio.get_event_loop() + self.upload_progresses_updated_at = dict() @prompt_server.routes.get(f"/{API_PREFIX}/list") async def forward_list_model_html(request): - return web.Response(text=list_model_html, content_type="text/html") + return aiohttp.web.Response(text=list_model_html, content_type="text/html") @prompt_server.routes.get(f"/{API_PREFIX}/upload") async def forward_upload_model_html(request): - return web.Response(text=upload_model_html, content_type="text/html") + return aiohttp.web.Response( + text=upload_model_html, content_type="text/html" + ) @prompt_server.routes.get(f"/{API_PREFIX}/model_types") async def list_model_types(request): @@ -100,7 +119,7 @@ async def check_model_exists(request): if err is not None: return err - exists, err = self.check_model( + exists, err = await self.check_model( name=json_data["name"], type=json_data["type"] ) if err is not None: @@ -108,125 +127,90 @@ async def check_model_exists(request): return OKResponse({"exists": exists}) - @prompt_server.routes.post(f"/{API_PREFIX}/file_upload") - async def file_upload(request): - if request.content_length and request.content_length > MAX_UPLOAD_FILE_SIZE: - return ErrResponse(FILE_UPLOAD_SIZE_LIMIT_ERR) - - print("request.content_length:", request.content_length) - post = await request.post() - upload_id = post.get("upload_id") - if not self.is_string_valid(upload_id): - return ErrResponse(EMPTY_UPLOAD_ID_ERR) - - file = post.get("file") - if file and file.file: - filename = file.filename - if not filename: - return ErrResponse(NO_FILE_UPLOAD_ERR) - full_output_folder = os.path.join( - os.path.normpath("bizy_air"), - os.path.normpath("localstore"), - upload_id, - ) - filepath = os.path.abspath(os.path.join(full_output_folder, filename)) - parent_folder = os.path.dirname(filepath) - if not os.path.exists(parent_folder): - os.makedirs(parent_folder) - - print(f"uploading file to localstore: {filename}") - with open(filepath, "wb") as f: - f.write(file.file.read()) - - sha256sum = self.calculate_hash(filepath) - print( - f"write file to localstore: upload_id={upload_id}, filepath={filepath}, signature={sha256sum}" - ) + @prompt_server.routes.get(f"/{API_PREFIX}/ws") + async def websocket_handler(request): + ws = aiohttp.web.WebSocketResponse() + await ws.prepare(request) + sid = request.rel_url.query.get("clientId", "") + if sid: + # Reusing existing session, remove old + self.sockets.pop(sid, None) + else: + sid = uuid.uuid4().hex - if not CACHE.upload_id_exists(upload_id=upload_id): - CACHE.set_valuemap(upload_id=upload_id, valuemap={}) - - CACHE.set_file_info( - upload_id=upload_id, - filename=filename, - info={ - "relPath": self.to_slash(filename), - "size": os.path.getsize(filepath), - "signature": sha256sum, - }, - ) + self.sockets[sid] = ws - sign_data, err = self.sign(sha256sum) - file_record = sign_data.get("file") - if err is not None: - return ErrResponse(err) + try: + # Send initial state to the new client + await self.send_json( + event="status", data={"status": "connected"}, sid=sid + ) - if self.is_string_valid(file_record.get("id")): - file_info = CACHE.get_file_info( - upload_id=upload_id, filename=filename + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + if msg.data == "ping": + await ws.send_str("pong") + if msg.type == aiohttp.WSMsgType.ERROR: + logging.warning( + "ws connection closed with exception %s" % ws.exception() + ) + finally: + self.sockets.pop(sid, None) + return ws + + @prompt_server.routes.get(f"/{API_PREFIX}/check_folder") + async def check_folder(request): + absolute_path = request.rel_url.query.get("absolute_path") + + if not self.is_string_valid(absolute_path): + return ErrResponse(EMPTY_ABS_FOLDER_ERR) + + if not os.path.isabs(absolute_path): + return ErrResponse(NO_ABS_PATH_ERR) + + if not os.path.exists(absolute_path): + return ErrResponse(PATH_NOT_EXISTS_ERR) + + relative_paths = [] + for root, dirs, files in os.walk(absolute_path): + # Skip the .git directory + if ".git" in dirs: + dirs.remove(".git") + + for file in files: + file_path = os.path.join(root, file) + relative_path = os.path.relpath(file_path, absolute_path) + file_size = os.path.getsize(file_path) + relative_paths.append( + {"path": self.to_slash(relative_path), "size": file_size} ) - file_info["id"] = file_record.get("id") - file_info["remote_key"] = file_record.get("object_key") - file_info["progress"] = "100.00%" - else: - print("start uploading file") - file_storage = sign_data.get("storage") - try: - def updateProgress(consume_bytes, total_bytes): - fi = CACHE.get_file_info( - upload_id=upload_id, filename=filename - ) - if fi is not None: - fi["progress"] = "{:.2f}%".format( - consume_bytes / total_bytes * 100 - ) - - oss_client = AliOssStorageClient( - endpoint=file_storage.get("endpoint"), - bucket_name=file_storage.get("bucket"), - access_key=file_record.get("access_key_id"), - secret_key=file_record.get("access_key_secret"), - security_token=file_record.get("security_token"), - onUploading=updateProgress, - ) - await oss_client.upload_file( - filepath, file_record.get("object_key") - ) - except oss2.exceptions.OssError as e: - print(f"OSS err:{str(e)}") - return ErrResponse(UPLOAD_ERR) + if len(relative_paths) < 1: + return ErrResponse(EMPTY_FILES_ERR) - commit_data, err = self.commit_file( - signature=sha256sum, object_key=file_record.get("object_key") - ) - if err is not None: - return ErrResponse(err) - new_file_record = commit_data.get("file") - file_info = CACHE.get_file_info( - upload_id=upload_id, filename=filename - ) - file_info["id"] = new_file_record.get("id") - file_info["remote_key"] = new_file_record.get("object_key") - print(f"{file_info['relPath']} Already Uploaded") + upload_id = uuid.uuid4().hex + data = { + "upload_id": upload_id, + "root": absolute_path, + "files": relative_paths, + } + self.uploads[upload_id] = data - if os.path.exists(filepath): - # 删除文件 - os.remove(filepath) + return OKResponse(data) - file_info = CACHE.get_file_info(upload_id=upload_id, filename=filename) - return OKResponse({"sign": file_info["signature"]}) - else: - return ErrResponse(NO_FILE_UPLOAD_ERR) + @prompt_server.routes.post(f"/{API_PREFIX}/submit_upload") + async def submit_upload(request): + sid = request.rel_url.query.get("clientId", "") + if not self.is_string_valid(sid): + return ErrResponse(INVALID_CLIENT_ID_ERR) - @prompt_server.routes.post(f"/{API_PREFIX}/model_upload") - async def upload_model(request): json_data = await request.json() err = self.check_str_param(json_data, "upload_id", EMPTY_UPLOAD_ID_ERR) if err is not None: return err - if not CACHE.upload_id_exists(json_data["upload_id"]): + upload_id = json_data.get("upload_id") + if upload_id not in self.uploads: return ErrResponse(INVALID_UPLOAD_ID_ERR) err = self.check_type(json_data) @@ -237,11 +221,11 @@ async def upload_model(request): if err is not None: return err - exists, err = self.check_model( + exists, err = await self.check_model( type=json_data["type"], name=json_data["name"] ) if err is not None: - return err + return ErrResponse(err) if ( exists @@ -250,33 +234,10 @@ async def upload_model(request): ): return ErrResponse(MODEL_ALREADY_EXISTS_ERR) - if "files" not in json_data or len(json_data["files"]) < 1: - return ErrResponse(EMPTY_FILES_ERR) - - files = json_data["files"] - for file in files: - file["path"] = self.to_slash(file["path"]) - - commit_ret, err = self.commit_model( - model_files=files, - model_name=json_data["name"], - model_type=json_data["type"], - overwrite=json_data["overwrite"], - ) - - full_output_folder = os.path.join( - os.path.normpath("bizy_air"), - os.path.normpath("localstore"), - json_data["upload_id"], - ) - if os.path.exists(full_output_folder): - # 删除文件 - shutil.rmtree(full_output_folder) - - if err is not None: - return ErrResponse(err) - - print("Uploaded successfully") + self.uploads[upload_id]["sid"] = sid + self.uploads[upload_id]["type"] = json_data["type"] + self.uploads[upload_id]["name"] = json_data["name"] + self.upload_queue.put(self.uploads[upload_id]) return OKResponse(None) @prompt_server.routes.get(f"/{API_PREFIX}/models/files") @@ -295,62 +256,10 @@ async def list_model_files(request): if "ext_name" in request.rel_url.query: payload["ext_name"] = request.rel_url.query["ext_name"] - headers, err = self.auth_header() + model_files, err = await self.get_model_files(payload) if err is not None: - return err - - server_url = f"{BIZYAIR_SERVER_ADDRESS}/models/files" - - try: - resp = self.do_get(server_url, params=payload, headers=headers) - ret = json.loads(resp) - if ret["code"] != CODE_OK: - if ret["code"] == CODE_NO_MODEL_FOUND: - return OKResponse([]) - else: - return ErrResponse( - ErrorNo(500, ret["code"], None, ret["message"]) - ) - - if not ret["data"]: - return OKResponse([]) - - files = ret["data"]["files"] - result = [] - if len(files) > 0: - tree = defaultdict(lambda: {"name": "", "list": []}) - - for item in files: - parts = item["label_path"].split("/") - model_name = parts[0] - if model_name not in tree: - tree[model_name] = {"name": model_name, "list": [item]} - else: - tree[model_name]["list"].append(item) - result = list(tree.values()) - - return OKResponse(result) - - except Exception as e: - print(f"fail to list model files: {str(e)}") - return ErrResponse(LIST_MODEL_FILE_ERR) - - @prompt_server.routes.get(f"/{API_PREFIX}/file_upload/progress") - async def file_upload_progress(request): - if "upload_id" not in request.rel_url.query: - return ErrResponse(INVALID_UPLOAD_ID_ERR) - if "filename" not in request.rel_url.query: - return ErrResponse(INVALID_FILENAME_ERR) - - upload_id = request.rel_url.query["upload_id"] - filename = request.rel_url.query["filename"] - - file_info = CACHE.get_file_info(upload_id=upload_id, filename=filename) - if file_info is not None: - if "progress" in file_info: - return JsonResponse({"progress": file_info["progress"]}) - - return JsonResponse({"progress": "0.00%"}) + return ErrResponse(err) + return OKResponse(model_files) @prompt_server.routes.delete(f"/{API_PREFIX}/models") async def delete_model(request): @@ -364,11 +273,11 @@ async def delete_model(request): if err is not None: return err - err = self.remove_model( + err = await self.remove_model( model_type=json_data["type"], model_name=json_data["name"] ) if err is not None: - return err + return ErrResponse(err) print("Delete successfully") return OKResponse(None) @@ -379,7 +288,7 @@ def get_html_content(self, filename: str): html_content = htmlfile.read() return html_content - def check_model(self, type: str, name: str) -> (bool, ErrorNo): + async def check_model(self, type: str, name: str) -> (bool, ErrorNo): server_url = f"{BIZYAIR_SERVER_ADDRESS}/models/check" payload = { @@ -388,13 +297,13 @@ def check_model(self, type: str, name: str) -> (bool, ErrorNo): } headers, err = self.auth_header() if err is not None: - return err + return None, err try: resp = self.do_get(server_url, params=payload, headers=headers) ret = json.loads(resp) if ret["code"] != CODE_OK: - return ErrorNo(500, ret["code"], None, ret["message"]) + return None, ErrorNo(500, ret["code"], None, ret["message"]) if "exists" not in ret["data"]: return False, None @@ -405,11 +314,11 @@ def check_model(self, type: str, name: str) -> (bool, ErrorNo): print(f"fail to check model: {str(e)}") return None, CHECK_MODEL_EXISTS_ERR - def sign(self, signature: str) -> (dict, ErrorNo): + async def sign(self, signature: str) -> (dict, ErrorNo): server_url = f"{BIZYAIR_SERVER_ADDRESS}/files/{signature}" headers, err = self.auth_header() if err is not None: - return err + return None, err try: resp = self.do_get(server_url, params=None, headers=headers) @@ -423,7 +332,7 @@ def sign(self, signature: str) -> (dict, ErrorNo): print(f"fail to sign model: {str(e)}") return None, SIGN_FILE_ERR - def commit_file(self, signature: str, object_key: str) -> (dict, ErrorNo): + async def commit_file(self, signature: str, object_key: str) -> (dict, ErrorNo): server_url = f"{BIZYAIR_SERVER_ADDRESS}/files" payload = { @@ -432,7 +341,7 @@ def commit_file(self, signature: str, object_key: str) -> (dict, ErrorNo): } headers, err = self.auth_header() if err is not None: - return err + return None, err try: resp = self.do_post(server_url, data=payload, headers=headers) @@ -445,7 +354,7 @@ def commit_file(self, signature: str, object_key: str) -> (dict, ErrorNo): print(f"fail to commit file: {str(e)}") return None, COMMIT_FILE_ERR - def commit_model( + async def commit_model( self, model_files, model_name: str, model_type: str, overwrite: bool ) -> (dict, ErrorNo): server_url = f"{BIZYAIR_SERVER_ADDRESS}/models" @@ -458,7 +367,7 @@ def commit_model( } headers, err = self.auth_header() if err is not None: - return err + return None, err try: resp = self.do_post(server_url, data=payload, headers=headers) @@ -471,7 +380,7 @@ def commit_model( print(f"fail to commit model: {str(e)}") return None, COMMIT_MODEL_ERR - def remove_model(self, model_name: str, model_type: str) -> (dict, ErrorNo): + async def remove_model(self, model_name: str, model_type: str) -> ErrorNo: server_url = f"{BIZYAIR_SERVER_ADDRESS}/models" payload = { @@ -486,12 +395,90 @@ def remove_model(self, model_name: str, model_type: str) -> (dict, ErrorNo): resp = self.do_delete(server_url, data=payload, headers=headers) ret = json.loads(resp) if ret["code"] != CODE_OK: - return ErrResponse(ErrorNo(500, ret["code"], None, ret["message"])) + return ErrorNo(500, ret["code"], None, ret["message"]) return None except Exception as e: print(f"fail to remove model: {str(e)}") - return ErrResponse(DELETE_MODEL_ERR) + return DELETE_MODEL_ERR + + async def get_model_files(self, payload) -> (dict, ErrorNo): + headers, err = self.auth_header() + if err is not None: + return None, err + + server_url = f"{BIZYAIR_SERVER_ADDRESS}/models/files" + try: + resp = self.do_get(server_url, params=payload, headers=headers) + ret = json.loads(resp) + if ret["code"] != CODE_OK: + if ret["code"] == CODE_NO_MODEL_FOUND: + return [], None + else: + return None, ErrorNo(500, ret["code"], None, ret["message"]) + + if not ret["data"]: + return [], None + except Exception as e: + print(f"fail to remove model: {str(e)}") + return None, LIST_MODEL_FILE_ERR + + files = ret["data"]["files"] + result = [] + if len(files) > 0: + tree = defaultdict(lambda: {"name": "", "list": []}) + + for item in files: + parts = item["label_path"].split("/") + model_name = parts[0] + if model_name not in tree: + tree[model_name] = {"name": model_name, "list": [item]} + else: + tree[model_name]["list"].append(item) + result = list(tree.values()) + + return result, None + + async def get_models(self, payload) -> (dict, ErrorNo): + headers, err = self.auth_header() + if err is not None: + return None, err + + server_url = f"{BIZYAIR_SERVER_ADDRESS}/models" + try: + resp = self.do_get(server_url, params=payload, headers=headers) + ret = json.loads(resp) + if ret["code"] != CODE_OK: + return None, ErrorNo(500, ret["code"], None, ret["message"]) + + if not ret["data"]: + return [], None + except Exception as e: + print(f"fail to list model: {str(e)}") + return None, LIST_MODEL_ERR + + models = ret["data"]["models"] + return models, None + + async def valid_user(self) -> (bool, ErrorNo): + headers, err = self.auth_header() + if err is not None: + return None, err + + server_url = f"https://api.siliconflow.cn/v1/user/info" + try: + resp = self.do_get(server_url, headers=headers) + ret = json.loads(resp) + if ret["code"] != CODE_OK: + if ret["code"] == 401: + return False, None + else: + return None, ErrorNo(500, ret["code"], None, ret["message"]) + + return True, None + except Exception as e: + print(f"fail to get user info: {str(e)}") + return None, GET_USER_INFO_ERR def is_string_valid(self, s): # 检查s是否已经被定义(即不是None)且不是空字符串 @@ -533,7 +520,7 @@ def auth_header(self): except ValueError as e: error_message = e.args[0] if e.args else INVALID_API_KEY_ERR.message INVALID_API_KEY_ERR.message = error_message - return None, ErrResponse(INVALID_API_KEY_ERR) + return None, INVALID_API_KEY_ERR def do_get(self, url, params=None, headers=None): # 将字典编码为URL参数字符串 @@ -559,24 +546,24 @@ def do_delete(self, url, data=None, headers=None): response = requests.delete(url, data=data, headers=headers, timeout=3) return response.text - def calculate_hash(self, file_path): + async def calculate_hash(self, file_path): # 读取文件并计算 CRC64 # 创建CRC64校验函数。 do_crc64 = crcmod.mkCrcFun( 0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xFFFFFFFFFFFFFFFF, rev=True ) crc64_signature = 0 - buf_size = 65536 # 缓冲区大小为64KB + buf_size = 65536 * 16 # 缓冲区大小为1MB - # 打开文件并读取内容 - with open(file_path, "rb") as f: - while chunk := f.read(buf_size): + # 使用 aiofiles 异步读取文件计算 CRC64 + async with aiofiles.open(file_path, "rb") as f: + while chunk := await f.read(buf_size): crc64_signature = do_crc64(chunk, crc64_signature) # 重新读取文件计算 MD5 md5_hash = hashlib.md5() - with open(file_path, "rb") as file: - while chunk := file.read(buf_size): + async with aiofiles.open(file_path, "rb") as file: + while chunk := await file.read(buf_size): md5_hash.update(chunk) md5_str = base64.b64encode(md5_hash.digest()).decode("utf-8") @@ -591,3 +578,195 @@ def calculate_hash(self, file_path): ) return hash_string + + async def send_json(self, event, data, sid=None): + message = {"type": event, "data": data} + + if sid is None: + sockets = list(self.sockets.values()) + for ws in sockets: + await self.send_socket_catch_exception(ws.send_json, message) + elif sid in self.sockets: + await self.send_socket_catch_exception(self.sockets[sid].send_json, message) + + async def send_error(self, err: ErrorNo, sid=None): + await self.send_json( + event="error", + data={"message": err.message, "code": err.code, "data": err.data}, + sid=sid, + ) + + async def send_socket_catch_exception(self, function, message): + try: + await function(message) + except ( + aiohttp.ClientError, + aiohttp.ClientPayloadError, + ConnectionResetError, + ) as err: + logging.warning("send error: {}".format(err)) + + def send_sync(self, event, data, sid=None): + asyncio.run_coroutine_threadsafe(self.send_json(event, data, sid), self.loop) + + def send_sync_error(self, err: ErrorNo, sid=None): + self.send_sync( + event="errors", + data={"message": err.message, "code": err.code, "data": err.data}, + sid=sid, + ) + + async def do_upload(self, item): + sid = item["sid"] + upload_id = item["upload_id"] + print("do_upload: ", upload_id) + self.send_sync( + event="status", + data={ + "status": "starting", + "upload_id": upload_id, + "message": f"start uploading", + }, + sid=sid, + ) + + root_dir = item["root"] + model_files = [] + for file in item["files"]: + filename = file["path"] + filepath = os.path.abspath(os.path.join(root_dir, filename)) + if not os.path.exists(filepath): + self.send_sync_error(err=FILE_NOT_EXISTS_ERR, sid=sid) + return + + sha256sum = await self.calculate_hash(filepath) + + sign_data, err = await self.sign(sha256sum) + file_record = sign_data.get("file") + if err is not None: + self.send_sync_error(err=err, sid=sid) + return + + if not self.is_string_valid(file_record.get("id")): + print("start uploading file") + file_storage = sign_data.get("storage") + try: + self.upload_progresses_updated_at[upload_id] = 0 + + def updateProgress(consume_bytes, total_bytes): + current_time = time.time() + if ( + current_time - self.upload_progresses_updated_at[upload_id] + >= 1 + ): + self.upload_progresses_updated_at[upload_id] = current_time + + progress = ( + f"{consume_bytes / total_bytes * 100:.0f}%" + if consume_bytes / total_bytes * 100 + == int(consume_bytes / total_bytes * 100) + else "{:.2f}%".format(consume_bytes / total_bytes * 100) + ) + self.send_sync( + event="progress", + data={ + "upload_id": upload_id, + "path": filename, + "progress": progress, + }, + sid=sid, + ) + + oss_client = AliOssStorageClient( + endpoint=file_storage.get("endpoint"), + bucket_name=file_storage.get("bucket"), + access_key=file_record.get("access_key_id"), + secret_key=file_record.get("access_key_secret"), + security_token=file_record.get("security_token"), + onUploading=updateProgress, + ) + await oss_client.upload_file( + filepath, file_record.get("object_key") + ) + except oss2.exceptions.OssError as e: + print(f"OSS err:{str(e)}") + self.send_sync_error(UPLOAD_ERR, sid) + return + + commit_data, err = await self.commit_file( + signature=sha256sum, object_key=file_record.get("object_key") + ) + if err is not None: + self.send_sync_error(err) + return + + print(f"{filename} Already Uploaded") + self.send_sync( + event="progress", + data={"upload_id": upload_id, "path": filename, "progress": "100%"}, + sid=sid, + ) + + model_files.append({"sign": sha256sum, "path": filename}) + + commit_ret, err = await self.commit_model( + model_files=model_files, + model_name=item["name"], + model_type=item["type"], + overwrite=True, + ) + if err is not None: + self.send_sync_error(err, sid) + return + + print("Uploaded successfully") + + self.send_sync( + event="status", + data={ + "status": "finish", + "upload_id": upload_id, + "message": f"uploading finished", + }, + sid=sid, + ) + + def check_sync_status(): + while True: + future = asyncio.run_coroutine_threadsafe( + self.get_models({"type": item["type"], "available": True}), + self.loop, + ) + + models, err = future.result(timeout=2) + + if err is not None: + self.send_sync( + event="error", + data={ + "message": err.message, + "code": err.code, + "data": err.data, + }, + sid=sid, + ) + return + # 遍历models, 看当前name的model是否存在 + for model in models: + if model["name"] == item["name"]: + self.send_sync( + event="synced", + data={ + "model_type": item["type"], + "model_name": item["name"], + }, + sid=sid, + ) + return + + time.sleep(5) + + threading.Thread( + target=check_sync_status, + daemon=True, + ).start() diff --git a/src/bizy_server/oss.py b/src/bizy_server/oss.py index 8f50d523..6b33fc2a 100644 --- a/src/bizy_server/oss.py +++ b/src/bizy_server/oss.py @@ -32,6 +32,37 @@ def __init__( f"New OSS storage client initialized: {self.bucket_name} in {self.region}" ) + def sync_upload_file(self, file_path, object_name): + total_size = os.path.getsize(file_path) + progress_bar = tqdm( + total=total_size, + unit="B", + unit_scale=True, + desc=f"Uploading {os.path.basename(file_path)}", + ) + # 维护累计发送的字节数 + bytes_uploaded = 0 + + def progress_callback(bytes_sent, total_bytes): + nonlocal bytes_uploaded + progress_increment = bytes_sent - bytes_uploaded + progress_bar.update(progress_increment) + bytes_uploaded = bytes_sent # 更新累计已发送的字节数 + if self.onUploading: + self.onUploading(bytes_sent, total_bytes) + + try: + self.bucket.put_object_from_file( + object_name, file_path, progress_callback=progress_callback + ) + except oss2.exceptions.OssError as e: + logging.error(f"Failed to upload file: {e}") + raise e + finally: + progress_bar.close() + + return f"{self.bucket_name}/{self.region}/{object_name}" + async def upload_file(self, file_path, object_name): total_size = os.path.getsize(file_path) progress_bar = tqdm(