From 1b48e54a3d2e5ede8c3268c30766fa5182d3486c Mon Sep 17 00:00:00 2001 From: Jaswanth Karani Date: Wed, 24 Jul 2024 21:31:16 +0530 Subject: [PATCH] code-gen-react-ui (#417) * CodeGen: Manifest to deploy CodeGen with ReactUI into Kubernetes Include manifest to deploy CodeGen pipeline with ReactUI into Kubernetes. Include as well README file for the steps. Signed-off-by: Yeoh, Hoong Tee * code-gen-react-ui Signed-off-by: jaswanth8888 * made changes as per PR suggestions Signed-off-by: jaswanth8888 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated readme Signed-off-by: jaswanth8888 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update base image Signed-off-by: jaswanth8888 * updated codegn dockerfile react Signed-off-by: jaswanth8888 * updated wait time Signed-off-by: jaswanth8888 * updated as per PR comments Signed-off-by: jaswanth8888 * aded react ui in gaudi test file Signed-off-by: jaswanth8888 * fixed PR comments Signed-off-by: jaswanth8888 * added docker pull for text-generation-model Signed-off-by: jaswanth8888 * removed unused files Signed-off-by: jaswanth8888 --------- Signed-off-by: Yeoh, Hoong Tee Signed-off-by: jaswanth8888 Co-authored-by: Yeoh, Hoong Tee Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: chen, suyue --- .github/workflows/scripts/build_push.sh | 3 + ChatQnA/docker/ui/docker/Dockerfile.react | 6 +- CodeGen/assets/img/codegen_react.png | Bin 0 -> 71012 bytes CodeGen/docker/gaudi/README.md | 28 +- CodeGen/docker/gaudi/docker_compose.yaml | 13 + CodeGen/docker/ui/docker/Dockerfile.react | 24 ++ CodeGen/docker/ui/react/.env | 1 + CodeGen/docker/ui/react/.eslintrc.cjs | 11 + CodeGen/docker/ui/react/.gitignore | 24 ++ CodeGen/docker/ui/react/README.md | 25 ++ CodeGen/docker/ui/react/index.html | 18 ++ CodeGen/docker/ui/react/nginx.conf | 20 ++ CodeGen/docker/ui/react/package.json | 51 ++++ CodeGen/docker/ui/react/postcss.config.cjs | 14 + CodeGen/docker/ui/react/src/App.scss | 42 +++ CodeGen/docker/ui/react/src/App.tsx | 32 ++ .../ui/react/src/__tests__/util.test.ts | 14 + .../ui/react/src/assets/opea-icon-black.svg | 39 +++ .../ui/react/src/assets/opea-icon-color.svg | 40 +++ CodeGen/docker/ui/react/src/common/client.ts | 8 + CodeGen/docker/ui/react/src/common/util.ts | 12 + .../react/src/components/CodeGen/CodeGen.tsx | 135 +++++++++ .../components/CodeGen/codeGen.module.scss | 59 ++++ .../Message/conversationMessage.module.scss | 15 + .../Message/conversationMessage.tsx | 41 +++ .../Shared/CodeRender/CodeRender.tsx | 52 ++++ .../Shared/CodeRender/codeRender.module.scss | 23 ++ .../components/Shared/Markdown/Markdown.tsx | 62 ++++ .../Shared/Markdown/markdown.module.scss | 14 + .../components/sidebar/sidebar.module.scss | 84 ++++++ .../react/src/components/sidebar/sidebar.tsx | 58 ++++ CodeGen/docker/ui/react/src/config.ts | 4 + CodeGen/docker/ui/react/src/index.scss | 20 ++ CodeGen/docker/ui/react/src/main.tsx | 13 + .../react/src/styles/components/_context.scss | 0 .../react/src/styles/components/_sidebar.scss | 8 + .../react/src/styles/components/content.scss | 5 + .../src/styles/components/context.module.scss | 66 +++++ .../ui/react/src/styles/layout/_basics.scss | 7 + .../ui/react/src/styles/layout/_flex.scss | 6 + .../docker/ui/react/src/styles/styles.scss | 5 + CodeGen/docker/ui/react/src/vite-env.d.ts | 4 + CodeGen/docker/ui/react/tsconfig.json | 23 ++ CodeGen/docker/ui/react/tsconfig.node.json | 11 + CodeGen/docker/ui/react/vite.config.ts | 27 ++ CodeGen/docker/xeon/README.md | 14 + CodeGen/docker/xeon/docker_compose.yaml | 12 + .../kubernetes/manifests/xeon/ui/README.md | 36 +++ .../manifests/xeon/ui/react-codegen.yaml | 280 ++++++++++++++++++ CodeGen/tests/test_codegen_on_gaudi.sh | 4 +- CodeGen/tests/test_codegen_on_xeon.sh | 4 + 51 files changed, 1513 insertions(+), 4 deletions(-) create mode 100644 CodeGen/assets/img/codegen_react.png create mode 100644 CodeGen/docker/ui/docker/Dockerfile.react create mode 100644 CodeGen/docker/ui/react/.env create mode 100644 CodeGen/docker/ui/react/.eslintrc.cjs create mode 100644 CodeGen/docker/ui/react/.gitignore create mode 100644 CodeGen/docker/ui/react/README.md create mode 100644 CodeGen/docker/ui/react/index.html create mode 100644 CodeGen/docker/ui/react/nginx.conf create mode 100644 CodeGen/docker/ui/react/package.json create mode 100644 CodeGen/docker/ui/react/postcss.config.cjs create mode 100644 CodeGen/docker/ui/react/src/App.scss create mode 100644 CodeGen/docker/ui/react/src/App.tsx create mode 100644 CodeGen/docker/ui/react/src/__tests__/util.test.ts create mode 100644 CodeGen/docker/ui/react/src/assets/opea-icon-black.svg create mode 100644 CodeGen/docker/ui/react/src/assets/opea-icon-color.svg create mode 100644 CodeGen/docker/ui/react/src/common/client.ts create mode 100644 CodeGen/docker/ui/react/src/common/util.ts create mode 100644 CodeGen/docker/ui/react/src/components/CodeGen/CodeGen.tsx create mode 100644 CodeGen/docker/ui/react/src/components/CodeGen/codeGen.module.scss create mode 100644 CodeGen/docker/ui/react/src/components/Message/conversationMessage.module.scss create mode 100644 CodeGen/docker/ui/react/src/components/Message/conversationMessage.tsx create mode 100644 CodeGen/docker/ui/react/src/components/Shared/CodeRender/CodeRender.tsx create mode 100644 CodeGen/docker/ui/react/src/components/Shared/CodeRender/codeRender.module.scss create mode 100644 CodeGen/docker/ui/react/src/components/Shared/Markdown/Markdown.tsx create mode 100644 CodeGen/docker/ui/react/src/components/Shared/Markdown/markdown.module.scss create mode 100644 CodeGen/docker/ui/react/src/components/sidebar/sidebar.module.scss create mode 100644 CodeGen/docker/ui/react/src/components/sidebar/sidebar.tsx create mode 100644 CodeGen/docker/ui/react/src/config.ts create mode 100644 CodeGen/docker/ui/react/src/index.scss create mode 100644 CodeGen/docker/ui/react/src/main.tsx create mode 100644 CodeGen/docker/ui/react/src/styles/components/_context.scss create mode 100644 CodeGen/docker/ui/react/src/styles/components/_sidebar.scss create mode 100644 CodeGen/docker/ui/react/src/styles/components/content.scss create mode 100644 CodeGen/docker/ui/react/src/styles/components/context.module.scss create mode 100644 CodeGen/docker/ui/react/src/styles/layout/_basics.scss create mode 100644 CodeGen/docker/ui/react/src/styles/layout/_flex.scss create mode 100644 CodeGen/docker/ui/react/src/styles/styles.scss create mode 100644 CodeGen/docker/ui/react/src/vite-env.d.ts create mode 100644 CodeGen/docker/ui/react/tsconfig.json create mode 100644 CodeGen/docker/ui/react/tsconfig.node.json create mode 100644 CodeGen/docker/ui/react/vite.config.ts create mode 100644 CodeGen/kubernetes/manifests/xeon/ui/README.md create mode 100644 CodeGen/kubernetes/manifests/xeon/ui/react-codegen.yaml diff --git a/.github/workflows/scripts/build_push.sh b/.github/workflows/scripts/build_push.sh index 8502ea6a2..58e92a208 100755 --- a/.github/workflows/scripts/build_push.sh +++ b/.github/workflows/scripts/build_push.sh @@ -55,6 +55,9 @@ for MEGA_SVC in $1; do if [ "$MEGA_SVC" == "ChatQnA" ];then docker_build ${IMAGE_NAME}-conversation-ui docker/Dockerfile.react fi + if [ "$MEGA_SVC" == "CodeGen" ];then + docker_build ${IMAGE_NAME}-react-ui docker/Dockerfile.react + fi ;; "VisualQnA") echo "Not supported yet" diff --git a/ChatQnA/docker/ui/docker/Dockerfile.react b/ChatQnA/docker/ui/docker/Dockerfile.react index 277546a9b..8ec70c657 100644 --- a/ChatQnA/docker/ui/docker/Dockerfile.react +++ b/ChatQnA/docker/ui/docker/Dockerfile.react @@ -1,4 +1,8 @@ -FROM node as vite-app +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Use node 20.11.1 as the base image +FROM node:20.11.1 as vite-app COPY . /usr/app WORKDIR /usr/app/react diff --git a/CodeGen/assets/img/codegen_react.png b/CodeGen/assets/img/codegen_react.png new file mode 100644 index 0000000000000000000000000000000000000000..f2417966b6d2fa75d72034116645b859ca6ee2b4 GIT binary patch literal 71012 zcmc$`cT|(x`!C2j9_6bDSV5)aa1>B!BE3dMMWm@H9U_Dxkbn?6B(c(qg46)9P^3fX zHA+dO_ueD{LWd9_frMm&cz*M{_s-0kHGj-~*K)ad_q*HEpU-}FLLV4tb052KjDv%N zTj$>GM;siSyc`^d7XNdE{l-4xggyJeKVXlvZ*dfK3NEn!_{;I8{!I>!;%F|0&0+T6 zN1xv_gK==2^4$OT$D{+mmxF_qp>zA@V_)m#3GNq9>{Eg3Q#93>e=c85v`s7zcguzz zSsoS3JnsN4dUk8oVYK@GNKqRsb+)Ize3>*&`A*azsgTmB0Y_GS(n#4(&d$zJJn~8;M}F%+ z*vsSKcv1f;GgC}JLdeaqX4>WyA74CQ`4;?cwdA|Y)k_6Z3Mbp*`gQdtKh#dYf1*zwIV=F~ z1bh)y)~gj${_9tl7n=v$wBStQ$AAI={t@CG7`bh8n zGTy%RYe#uNNMa;y)J?J3NG8|)H|6%sdIOz$Nj82jhEEA=ZV_C_$jFldisdWkP96+0 z5A>e&Bb5T0HZpZB8X6sk6z~G5-s}I)pzACwrp2aEc9bdB)+jGPL+a2$({5F&_vjUF zp4hOU!%n`5XPC^L5R<9yF_D922yaZLuEe0a-sp8J`Yum#wiz<`pruITfQ5?FHtq)^ zz)=Lh=h2`z{k!Xn^%DnQ-aE?sTSh+oZ+P`ivp{6gpfEH!dC;aIpL^st(W`Iy-iuSb z-T1jWac2ZZY$^_JF8W;LI{aQ77n+4s&74pN#^<;3t>Fh5?l=44Fz1wjjZpXDBErv& zpXXvF)TkvxO?hL0h#>9}f!~x2NzTm7oKtWaY+3+9qT&-wH)Rv5pDFm`BK#3W5z-o|Z6r z@A$~ogul|?&Tcfmv4|D2KCSD9K1qtwkPOP0Z6p*GlNJ*WRB&EVfj1xc!QSIQ!X<2>ZE(8&sxM%mn-*?iW$FW#dB=3y5XAp0ToEi zptR>Y?e$Rq2zs#Fc@5e`c9(@rPbiP@Ee`q$I^|M&$NN{LSfMZiAQcj|RbH#!- zvi-X8_8xy`^D@5V=z(lpJ<5Bf??jtx*Ilk4?kL5xf2x&9I#^v(Sq!7(KQyx4ejV8} zKAs|w{F(e6W&6ne@b^1?wyj=U`TB{PnrfNwPx2cAdpl~x7LypNl6D_gI5D$Cbz$uJ z9V@-m$3qu1d4%};`18UtJpsvW+n+=Vc9z?HuL(?DCw+R#)&?cf%(HJpzX0Xb_^|J$ zqUSQ7xRXfQjxY~|ziWGC9rRGm*Mht5J&D3TRl}Q#QL;}x7BPYpHh=L*C#xO+1k3*e0`HV0vO?d%)#`COeu!*s&`8`5ZH%o10ynGfBrHh#} zq%<}prgdByDuTEx4j!h;UvfO~dQWo~DAUvHO6ah{+UGV5YDrde=+atfag|MH2_PyL zLp%XzUy{lM52mgfXkn-XA#Po40TID3340(eDg~rIN(-jZeHqe zsiG#8uvtk>k|hrnGX9o;ZuOWj)ez+j86t*XCK9fwiH>^|2{uR+v@bSmr=wbAi3tOo zj#|M{9k}b!`8k&1u3AQBeJNn@vr*os(0<`mzEabzTNx`#O_<=49>pMBH)ixT|J20e z$3{gP*Qo7*LM>neRq^{4e?mi&kV>Ksd&NtU2|O1b8M|zxqX;d>#-xR$uZ(FP2yWM6 z?DX^}H-_6dtKFUKBZHKXaGIK`SC-JKvuM-DYfcN98ic+Sta$d=VE^nQ(Y!beA( zmPtO##T-5%(3^ROqL^oPx=lElzeH0_rJ%(;Ldbn)xYyynTapl$u^sV_qWU$UgCjBf z6PQ?3>eh5p79**G@}s|cb{e3GaBO$2aO$;@{Ve$+1>V)LNS9K#_0Utp<`oYFRV(|< z*vL-jQV8F(2G8WXXyy4io0XaXOT3$YTn;r_OXBAe?!p}ikbkJ=dEvC0xhdkCL;THC zYt#waUcs!_%;>1fWC-$Etr)i;ki4O9c4bB2LM#*Nt>@gQW1}TmxjL@wK2tOdB_)5& zZsD!r78c|L$cc3BIa|4WRd#;F))>XKWPW~DM`oGT8J^Nu?Y}3q0^d78|1p>`(LL+v5ii(OjG@6Lxv-t;_U%!pJ zq?i{P1e6Y`&UI+2jd=lk;EHr;{-8**VY7evjk0&s&tWMwP6Y+FgrAHB8<*b}2#$!< z5hZ~UDfI70-8<^qZ1r0TB*Qt~(C0~$4@PVL`Nhg_gWci{jdGk*r$EDNVdrXzx(k~x z-K3+ZG~lMS0gZ)V0@J67v|S~gHd>Wu_TyL#JK%?tF{LVf>1IZ%>*YGKOIarPO2zMH zWp|rYK-HQ!zB=GhE<>SW3*BKw$Z}~S=z}1ShKsi)D|>KBdH#zLVe#R}?HrLgn7xkI+lC;6} z%M-n_?&N$SPM?dNX~GGC@~E%R9z@Uw8!9Oc7>CC-1SpZ1Ii-1_q^30Llk7%&E*L_t zCv}ouaTZ3CQ9`%>{Kc8g2eO@MeI!NTSOcgFe@uKhdfjDU-G`{Y19Sczd6n8mh|nL*f4 zA1DoKd_R<}T7WQ3(CibKm+%l}lf^Ni3~NU5X=lo0uK1&A>*$*)e9A^i=; zKS&@wnfmVJ5ir>E;d4dX{$XVj+AT~CM&U<*A4QC7pdLydU5zgY0|j2JSb6wXDmtX+ zbXL96Q4ov6f6$HHOFSFP^CPT{ z`@;EHk0sNshA-ss>RfPZQ?cxJ2;s8(vj}w?NCc1-Q(3>tPoC$>CG=E>gE!wnyfe4w zugEI-sB@KgMM#=AcVF~aQl}Hf>HN4Hr1aAIK}hvf;_H5tTq`?XVB{kqQi|X z0jym{MzI6&V_qp7iCOBUjsC&kR56KgWh_Gh5xj!WL6X@TVg>N&K5|`Ivwlk{OXLeK!Z z<-VHt_=`L4l=T*JmmAB2K_Al;irq#F6fPHOk6v0hD}xYmogn5Z!-i{V$U``XwPy-0 zT@*rHBl7f;%j7Al3{d(A6w*F-Fv+*=jG?!|YJ;`s6S(W-S2gVP)S0_#1R5S8;1!9C z?N}M6Q)y=HLgY`0&)1>kstPN9^vD`+*pWzaU&QnQ$USI>%(DA{JI^3?pKB z0{=#U0`%Gu-nT571kvI(R96P&V78|L{Ywpvuj+ivg@%?{NwmqEj<~udgNVMgTJcp& z`ZUB9FLr$QCWG+~8v>qW)+p~meVS&q>d&lAt}oGATB-O-6Qtmp+Vh9)V_;j$s$B{> zlElQ6_Ya70Sq;J?riD0H~AS=VLX(o%*Bm;uGDOP~d<4a@Y zF(1M6uX$V}dM*lwxgD=o*2K74FSF*tXu}dy(LMUu_V|OOp2HvRYx=zX#Z+^?n*e~c z3I{WmU(5baSteWuTdZ#bG02y(oCbIMX!BSp$z{S)hm2IeKyde() z8Tj-kgVr7Zgf{MkX6>|E3FyWqOK2mFHvDnqzaRrRTv1(; zM*F$eyvCd`Oa{Pcn{44F3vQ_m!Vr_C_c9mwk*FeSb`a@kUo_ z2rF++JfxOmqo~JC6Fc^yFm^7yqFZ>3*U--kp*OV@?mmf_3I~p@qiB0Z$WsB*0V5Wxe{W|6BFE(L!>Oq zZvhMFTS}JNBe%v9-(;e*%Kns@q@rZWajYRq_R1ffEf ztUl7O9}mSyW*}ph zh>bd12sp=|U#n?g;q4=6Ob_<`*-a!6Ut<&Me#=(5Vcm7MY`S%{S0Z=ZWk34C1?lA( zU(CgzFNfKTTPuHPA*Rh?tDAYI5@x@+2N{S_V2UF$V~=4P1G_MQHtm zj!w#D$!?F6XDeCnhy*#hr2yNWB>mS+pgL7`z2oDR2f^=J(!*kh;P0{?O-AIJQ6GF{ zSE>-m6k*@a#9|}ac0diWvlvj~bGzM)QY>$3S-DK3P0IlysOsgH#aLu4v82nIA6Z3z zb*{RvgzR_-A;jLZ#bgmQ85W>S1t2-QIjDmT?l2(jm>cyEezF1PEDSQ;H}#tksCF{noO)K zuW8nlfYXB=S?0868>R1`Fh=sS6BVvSB1B z(k!<#Y~QVKAAlwtA2s&1bIR&2ZX=bqH8V$UbpU{mHc1n7tl!w9HE5b-H7*qPH`m5x z;pjswNsAm+Hn7O5v9VJ;P>X$+a#ol*^sR+le`tv)5nvczrgpsjelBHS=CFuaPTA?W zF+xO{mzk5-pb}QJ0Kue?6QWtFOqY(CyfX)TVA}}HNUz(P-%UXvqk_ElS~E3nGsR3M zjUNx(MpfEuIbu#7d^xPSHLA%wv{IxU=v64w6cEf3a~1M6Hm>$V)FJdzDD6@Uw55)k zrk3a_0LXuJ!;~`jv-Dp}?z zk-mg2Eesnh-+w-4W@l&nbtEge@A9I+v(xTkbT;>>lrw~Q-jwJV8T@=O|tzC67(B} z5`})lZViphyT1RvSyJkV|GjXF`2Pp!bhp?s%}*>|+A6u;``_Lid;OFyAfT{**I6~K zR;*9xr0~H|H-M!!jX}N(83~x|T07M%Zqfgay!*)!4OdJ?p-TSehNN`A-XZG%Db&@t z4;4d!+5h&qHz2Q|pipR5B4)s{f#q2y#9=!AJIJo_g^gLV1GvxS<%+4sl>cvd@JF;W z1QH>h=KIgD9sUnn`S90UZf zWMo81R&XI~0u=GBY#ldrIy$)_GXCb`h&UXnj6lm5K*%@-Il$P}>yn zGIMTtg3-;&> z;Z}eCk7M`cZ%-^#?U)QW_8s&?IZDV|j0$#ipa^KD?Nh}G;3a&lpo#8_|7bkj%-{n1 zw})5p%G-(F;p+%%fo0+wbhRCSTUwN3uDr$WkoNI;7+la>OeJlmvR^gbF)|9ah{M_B0})$vxjPZ(yZ)V% zI5x%fR#o5Z)^aj)ySCY$Evu2)JW#jQG`%+p?_iB68eRQao?1QCGkSS=E`UL@j zK9Tc=t*6+g2Ra_GR<}Sak1d2NuZ*6T{ZczO>ES zU7F=cS0A^&RkpHVN`H@}S0Hsk(drvNXtOfN^K4sUd;Eg@WuX{yx@1xUZP5gqT<@yu zw9=^3XKQW-3f`jXg;#n&be<R{7flc;Y_fawjyk~Df$>LaDH!f1(OT8u_I~B>? zDQ=U7(r5KrWBfuvdy6K;PJNl4fn(=SUKE?NzW=Hr$aMR;;z8P1l;khQUYE;sHhkET zG0lq%F#*99gn;uOU-u5(KSmiWuo6Hrh=4xwr2VsqA0|=D<9}SV4gB5vK}$R@Zv~Y@ z|HZKQ@$=U&3AAy(Ub&K{)2SIQsfa(loVQn2BoRUIT)t!ft?*b{1vig z!X17f`z7=1FN$StY@14Z?zH894Zjk5``{$-Mw%w?h^y7}?}rzbhOo>n8aW!762(tv zZ!WP@H*ii4tpf_k`XswhK4Fp4T-{c+N{pV!ge4X zNeAr9{vVIhmN31mR}0(|B9z=(>*VpVtn2C$%PLghmiagXScr2GP%GS`t{(CZg|Q&i zVen9LUOJtJIpo-TLd@Cw2lt&N3E{OM@yzW`bJx9=AZ`J|dLwVNI6ClpDl;5Q3#XL; z=_S6zNF~B_Fd&M)h6PHoVu3rcrWiY&WlWp&g8VPNy8$ZlTom@-YQ3m7yEFfsux9yg$2lrMrYW-%*2rg4i|nG&dJbW-S6!6U^&ofI&S_jV=D|7< z!1BGoXY4WaUK^KRF_|0`aiG_5oweh z6I1@uQNU+ImD~L{&2*?jjM=Qu4#7figW<#Mqjo}&fNMz2TD=;Af|^PPydy@bNo%gZ z>l zDW7q7?L70ct7-7<$VWDxwwC6s)=Vsd6Sp zmk=gVaQRoS$$&K>b~xg_EBtHCFPIQYV`Khs;nc2DGI6L4WEeavH-fvb%LYmj0+3(!^1@r6w5~)gDJ#wWAlV0m*w2IXy&yc3#<9wz_$Ze1D zEu;Wpisk%VwT|^oi1j~(vWFhY#sV#Q*ZnX!#Gy6wsEm=I1rwun8gV@RiHG>$BLjIh~fXoyGdErIat@{5G117p8wtRtGe9U-W49j<@Xa-hHeHWUg~onW7k;3CO0 z{CDkUC#bwYv(s2Jrn#gl5}TLapE*8&* z)&iw3yjf3U&={}9x5f{-YfW5ahd2;9tmy#W?B>Sg)bS|8G+5x{Kyyz;AcgLy)Wpat z34N*kL3%0<{3|AT0Zmt%wn00Um&&R2-xidEkbJvd);tyztApJLT zUe>_SP*&l^yPg`|(zTA$4R05g1}juonffli6<$)2)t8>vtTSkVsB+U5)+d=w`6mBwpSQK&I0S{|1ePfh5O8Jr-tuMoYIKKzxEp^BPXtO{_% zGjJEpqkP7HY7=TG$9RVe1i5}`!KM`as`~gRE(Kala?tF_>1<@Rg&xqa%QJ8&Fgy$5 z7-L=@I^jJg1z!nk2rw?3R21ofQJInmC0f4w1I0S8$Os)#g{`}|P;K6bEI`|zWhCK{ zw}w~+Tv>m;z$tM6brjJ?t_=$y1W2EG-)pid-tbK=Ty=tW#4P+np_IbCcb!BOME z1y|7ypZEl?AxdPi!_S}RlJzsA)a7}?ObAs4kdI^dq`&-06;m_Lj+9uK`^0jfGHra( zS7ke7FKkSH!p*K0{&UoHqAcDu{L=I+cNO$Z5WxPE^FN@AnAPb#2fH4|X7$-tb7#$8 zj^{?#f{_AmZsbpExCVAcV#KA&h?TC$Gv-5rgsrq7%>H@oyAsynw>GDf+!ADF1Lf!tN;lqH`pO62UJGfS&S zOO%iC-SSh%op1N3!qxz=2eT``EXB9@{t~h|ufotyA<2PRp zr@fT5n}W#yQcIuMOdT1?9Gl^#s1l4u@Q0v`v(XtEPJLg^PNxGwNoT!3hL-_;DeP0- zd5eyCMI}5xV%sB-R8ffIWZ6~Ff@Nz=1erTD4MG}hJG72-GrRHi^<3iWR9=KB{a#?j z2~{f}ka{YLve{K}2jHuN4ulax(|0;BKc8bo0!p)WhK%{tadZMj)W$+atuy^-Us+_& zJALE9$>PWHZz4cUzoyE(k=`yWFx%|F71kM|?+>LRy^I(?r?<$(Uyd{>Y06(_y0;6T z4aOq7R^7Wlreb_8%!8=SH>*@Q>wrp=Hd1x`wp*hRd2w!PYx>?9W2vKU ztKmPORK|V%IOAjE!U)MUVP*Bnq>Kn1qb#TOVF#=dNjcyfLd&+xS2Rt+;1=5Af?*!q zMKDbvdz4xH7$eVI-~8q~lz!D&{FgL;Qye7a%)vL;Cv10H0vwEP+;MvDb34U!9(awL zDjW(@PtgZbZ?p$|y==AI-pJLb$zw5S;);Dw-hL?oM(9d?4}>O4q z$r#^Oti3$sCmwFmq=rWWh({`f-FnrXoFdx_1a()(1Ls3Rg6HhoH28vD%75WqFRXL^ z7jJFe(J^wbI8QRE!U-(zHhRVoVRO4a51>lh_h-RE|9B6}`Y3W0JG}KRvA6a*NR=Ag--VN9{^ABOE3$+c5WZEbz;EKQ+ zU<2RhADD_h@LL@76Z;wKg_K#QuJghkiIvxKj9xyY97EJ8(fa=P!8m)jyMluso>ow( z>gexpn)NAMrTk&LpXjr*a$ytQmEQ}UAM;mK*K-ld9H8_9-*S-A$ng_h^^CiSB+Y+D zrtGj5uNvj$+oNPO?j8)w{{Au>r|9$kq4>}!cFeT?2mvYMC(2|9wAn638#i0&4rP8H z$IZ<#JnC*^fGr47dcv|J2HS$*2WaO!54LE0nd zGL;lapxBKW1r!P`U0bo9?BruH(VbcTR5A5K7Ufvq&c|!e&z`{y7yaeLjy5SHecxdR zi_`$y#iD;tXsTV=5$1@g#YOEXJ&=Haz!!E=t+R_u-vVJKHz4OIF2)`+8{;3~#F2oS z{N#Wi7oG_c^6BijcdOdYKkKuINgB_HC&rTe00R-%s#(*=g=fLEFn|_jdfQ6kDtlKu zKd-6T-@*5Pa*jVhV_sN4We3*YXWu%!<&c7R1YMFJ7dd)@a?`dWO6^Vu&xr4Inf<-Y zwl)tfjAfbDPJl#yNs;A7^0J|;`Uutz*taLO98jSC#8>}p^g%RvW2*1R zBMEPwS9OT@=|G$8OpYm>>>CYHn+-$alB>2N+@g+R6Dr=ot>*#HR?fRu0b_^ zL;drH-ao7Z?mhfw%vWCa7ffx&+DGyYOs4~z9@=ep4+ooVVc1=k9J_9J6O<72naZDE zSjK0&R1Ract(lZVs(Dzt`daQtGs@_t(oeB>sZk+4yd09ABHRRc@*8 zYTfqWa$C?X?uLTnvrAtqnGnDuozg^TrjLuEo zos1dy*+~nd>8`oU)`ruD0xQe9*5YGpo3u}?g*t_uT?BXPWIAwYSPwfM`L-v8J&C|# z78{dm3_L&TxEa&6JJ+9{^OT+=%DwjxE?FsQ%h1N-y7STvpOMVwlGu|K?ae1S=&Esd zt0tmrD`KhqoJgy^zE1`fR?YNJShc5*+M%vdx_9mSx&bMr*OFzVa(J&V#;~D{rrI%n zT8wA-1)+qQGytq?*$DKd;tNoS>xPdXAKM{sYHOSpHWocPs`{U?V>hj{UQRlE^%rQV zjhZ{uoe(KJP%ct+GB_eSu|zW7{s5vJd!5F`o6Ez6=x?W1w^Qu{9@~lUWIRJ8K zW%FU;+7$F|zhhrE!6C;SC-8Z7({3aWh$3v+6C1@k!V~3Gf@X0pt;tpQQI+@G!Uuca ze}-RPdtI<~4g;^wv0I~H;HqnDG1Q1qr>f6RUrM}6%`~U_ZQ2yC<`)*_N5#ANpxZj# zYE;MWo~6HYVx`0__IhO#RPmqBi!QmyVKtzWCVuE#je>^ya>)RHtLPwPzJ8^br%V|V zn~8tY|Cgm|s-%gB^)V7AnXZu4pz_tzOHDmXit4#kH{=mrTIDb>NeX4imQK3O_8mUk zX{I*T0nm0tZ{1cK60vYw(1-&mP851~-67XIQg~7XDIQ2MfN8DeBp<7N_CrC*hGW5E zmqF6T9u$^3W*OZ&R>+hy^G2F@fy%6730pqT9puhVnjk=>medWHsUNmFAdQ0A%YHtU zxg@2Mxk2gowy>H^I7anVwWIfqzz4UjuUKSGbUirymY&lHc4bY@uh)01Hn);;A*pwU zu({M*HxZzXuFUlF%QG568mC7KWRT-6%yNiQjo56%72wGH5w(M%ED&a1HyuHoXrFQ z!8@ltsNV-b!330*~gB)K#F;g?K3gkZ#(03BIOr6(YESoZnT;gMM4(WmqkxBvU0_1$?_FDOr&L zgoV!d>xU-oY|RF}H4*FjNRS!J#dgka&;?ekNjX5u079&fG@fW`S&?I3_10uA4W0Uy zGu)whJi5F-YZl>lI{wTEp=)auO)AhuSbs>%va`rnbOgnKp>_DaEwQe|KJlg^d{#N6 za0PA*C~-x1SJ#EL&3G}+x9R{%eYSxffff3#)RU&ulg~Qyef3YK&9F6?-s;INkHDPcm3~lsi{+JY^+>7FPr!n7zByFLH?YO?8 z-j-?Ey&#tzX~zmf3eT=)U%I_AD91KiFY*gWC@K9{WzxYeZ`TX1ZKih&8&^bemzN&{ zC1!yl45JrJ?n+u4({5b>#7D5^hZ+3d*?uPhsUzeBtrfF}{3f9Iz$B!O3(ezOdZL+< z=A3@=z?^RLqZV(IxXs+ez@rI)ei|#I41O=Iu9ORXU~@~WjoARWz$?bp zZbq;rnN^}R_dkV_OI$JwW8yoHpq(>`PqytL8?#TBG_>P zrFQ-)Sij?RwIULNda~B|*-Tc&N1s2wP<-#tq8|=c3CDW3L`j%U-KLGDCuSR>_C1K1 znZl)7Q)6Yv0b!F3`{lA7nN*1hC95aMfg*;tM9_m-Rcu0arf+-bJSdiiJhxFD_X+^hBI9nwFdJsLy4#o3N538NK*+ z0dTXTX8L2g9^|g><=xYKExIHjNuk9TWTD5fgn)}R zS>}ynQgj&mGRzApm!&PgN;44)k>RD#PpCIRn<6_h?H1S{y4Z7A4@e5^2ko=W2lhP# zm6Q0W%gmg-70YvthIjqf+}HnoYjVXENQYm`z*7Rx4K8Re3`4*wDj-H=FLR& z4SNr@)>k377j`K(XEtNQu%m5{^XeYhpn1=|mykNvFgh=}oQD7`t|b{)#ap@pV&trg z{Dv~}%SCi^d&HmYG%tpwJLr6B!30J&G$d5AS)Gw=dw9Vs-@JaOEYSCnz4E|9UH_d~ zJZe(=34%{e*(8JH>5P&gORT_l6#MAuCCOGAPs5=~^zqU+f0;eMw!J*AB3*5gprI*4 z_;FGc-3FydLO>`|gM%f>c7Z;+qbHuj=Hgybdm4;C?#P3x4BOkMte>2GmaZUD;&`v# zta&@itqnL-6OdLTT8E2s*%DQB1GPqbu}jUbQ?JYV2yGk|(rv&tz|>tfFpNo8s@w<0 z@vkQYy*=wT&}icwaN%R4TKQ|s%&FjNid;R$nOgRgA+lomvSY{W{a-#=6$n?{OeakH zQ$R(j3T`>=DnuN9_wnRZuj77NIMy)8#e2F6nUbjm8U#eWTru3un6FKw-`McdH}0ED z@7a;*IR@Hv_&zoqBU6dThj`cDSk>E|4cM?pu2On8-r8O-b#l(5w>D;PW8WGpR?MC$U9@Yx$0qqXe6H8ZJvg!G{&#*Iq1z(~nDSU{_KRHr8LA*hVWJ9vaUW zWbkoMdh||RFw{z1ZflCMk$aUyFhAjR+EfTWjXjSsSJ%iXfj+L|9dV!ZM0@sNuA!t0f18qCBu}3c3+h z-S||O`P!tv`GZc?QbKKEoqg**jRyH>>KWA=5%-b}skaLg{`IlRb{S`>yzaOGHkM77cO&d;fj33oE#-f*bejUs zzY3J9voHSmpifMy0~xBYl2*N`G+kl39^bcn7;fAx`u0YVDKnhedqFn8EM(EGPL=y} zE9%H{ec;!Jqi?LlT%27fT_&7k_nu&R7x;JW^;0u?|4tv6vq~seH3#h;hH!QOj31X{ z*~-k+o#RoTd=r}(%K6YXZYrRlsoW2L{S`Gc&wK>S7 zKd35LUK;E<1Psr4uMu8D{befC%At*ow#_#u%8xDiH)`H6)WN2k3BufD`b%4Mv9|+! zU0V|7t)7tEX7Y@mqUw{3`^rJOoS^9EsVgE7xNUKz}f^s;g zYiB~Q7Pz;}OfFP(FK+-Jkry;7iV}M4T@>kAy$e6*Ki5CLmC6UcU+>!?WilyGaIj2h z9opH?VKDW*=1+BCubftbT*eBU6A&-WG448TYgx_b?9 zp|q}|Je`p+5DUKFqhjDcy_WGIYy0Z1wXv>7ozsj7xW!^N53}S$-+OWpalEXWP?U zm9Cw+Cu#*(EC%F0cWA`&!jHiT&`!si0F1e3^G#D%K~BMM^B7ZDfN!R zMtn2(=!!cvjhm*XjBpVR8ugR2rxU26betqc&6f1L`-EZEZ$QJ^BhTB)AzayzM>o;m z(ATs;{m0U?`Fk0&V*(#Ry7kx4{FGMg8QuEZs8|UZSDw;b^LjzU-JUCfH&x}K=FsW5 z7Jj=OL_+WMH+$LgzD!JvSXm|x;|brfX}9kK+hm9_c@obU^${-sPDjar+?M#G7yPTw zIM1YaT#}AhcuUP{+>b4t+Xf!EI0`(Z-6~mxU(TuO!HmB;hx%ZzI|IdeW&5VWEb8T* zG-WUwjBAkI;Gk*uBWnfl&!H2_%VgM!5A51$!$=pyHG z`X-hv{gC+E4?FU&0x3_BVkMeVU*;+AkDCBI4B8aOS{pJyJA$8}sZvbKsgy3G_gB#; z!*x3!BG_ks=|LyWts&sjXGrB2@MWpWKW#n*vO*i;yXJh7(_Xv+ z+lpGC)=$j$u~RZZidg4rvDnB9DRGux>Iv}MV2-i6-P}<{c=eNA1lx;$G-}ZLXm~&Q zs(#MHEcjnegU9>})2|TnWF*ig9LMeoJ{t9LZtDt7TcxuvR=hA+pcEzal0?{3upg8b z`d_HI;{X40xQuBV7!0O*fYfn7u>HU5_c$K?i%kD5`oH1Tn|eF;h0?Ftp~-zqpG71_ zZ82W|tvX6*T4Ym%o3}mE?=&7yQ-OiMhvhxD&}qaSeK_=*-t&CvP1@0eqMH|CZ-ToW z<@alHnm5B8aBtXUN~?z~lpp;!9O)MqzIki={s-R7&Qkn3H_xd#Ka1F#rzQH(|5Vuc z)&4??eGUM>y{FOlPl2Go7Z_118hwID(u*=XyDP4T+SS{=BY9l312Kq?Aox{%%7|{f z4;PkBeu#nScSMX|xWER7#%V8O!Pcu?GlwV?v2pX^`(x04%O~iLwp{F035m_=MD}6M zkJe!QWd8SRoi+=MH0VEPf6aYD_!i(HfJ-_&5%K7h(464Eu!Pg)guj9t#e@X7P^d2| zLdMO@7fX_yzAB0F4du7a%?9l*jMN^B`T2a4kXTn+RIp$Ea~=FlkDW@_1$swx?Oka{ z2h4SELb)rfQ^y`J1@z8J3mBb|cJQ!Q;J?!@DVZ>q;1?S-+t`+Obl`~>@uFB~)RTql zFmUf5I~DB2l>KOEv5E5i^I=)37%|dQ2;mG>Un9p7&ikt0K8B6LDR5=(o zmp!gqh3(Du)Yt(19(hQ^xY7J@x2vdFT0Hz|m24=lUw6U*mqPI4z8suqbnWB57WF8J<7Gi==6( zLL`1kug+<-*$ET%boig0NdoLEoLu5D-7=}zYnkE-oVvHLdDw>Ky1u(Zj|(zP-)(6m zZl@*&MVXBo?4%KC54Zz?1q zb{DcrL^r-qpVSt7&od@=0}^t$B7q7XcAif%Iw-Pye2haIFRIh=-Bhsc>R$*Qspj;% zPsi&2BcXE(|8}ALYJM(wtN3rHgCjeR`K>p>kOW2O=$WikZ*vB_daw2VM$7^)d|qpT z(ZsGOc~>tMhObDzQ)0W360#uvB#h_u7ck@$$7KEA(6mx!&i+-_GQQ}@lbkbR`vFyq zdC<-SH-^YX%)%q?iq6aV_La|W>V=bcyhJgYXLMh+S&q;!nB835%;b>94uZeU+4#5k z-Sz$9?ZmE!B>%V=olXSMzHt(^$J`7-r*-_UW0k&Q#X~1D@xu>y7Vh6g%WK%O5sSEO z(C{eQ=K67zCq$B6zL+scAlw3lEL_`Hn#GTPJQX7Hl_MZo+siW!*?hoe0Ek^a{S�+xM4j{=OQuhz&IC{MpBU1k?w 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%0A_cu?^Dg>G`GmN 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 m36F@dQ!Bk0zVjl6#E>Hh)`>@LLs literal 0 HcmV?d00001 diff --git a/CodeGen/docker/gaudi/README.md b/CodeGen/docker/gaudi/README.md index 05f0d2056..614e73751 100644 --- a/CodeGen/docker/gaudi/README.md +++ b/CodeGen/docker/gaudi/README.md @@ -38,11 +38,21 @@ cd GenAIExamples/CodeGen/docker/ui/ docker build -t opea/codegen-ui:latest --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy -f ./docker/Dockerfile . ``` +### 5. Build the React UI Docker Image + +Construct the React frontend Docker image via the command below: + +```bash +cd GenAIExamples/CodeGen/docker/ui/ +docker build -t opea/codegen-react-ui:latest --build-arg BACKEND_SERVICE_ENDPOINT=$BACKEND_SERVICE_ENDPOINT -f ./docker/Dockerfile.react . +``` + Then run the command `docker images`, you will have the following 3 Docker images: - `opea/llm-tgi:latest` - `opea/codegen:latest` - `opea/codegen-ui:latest` +- `opea/codegen-react-ui:latest` ## 🚀 Start MicroServices and MegaService @@ -143,12 +153,12 @@ export LANGCHAIN_TRACING_V2=true export LANGCHAIN_API_KEY=ls_... ``` -## 🚀 Launch the UI +## 🚀 Launch the Svelte Based UI To access the frontend, open the following URL in your browser: `http://{host_ip}:5173`. By default, the UI runs on port 5173 internally. If you prefer to use a different host port to access the frontend, you can modify the port mapping in the `docker_compose.yaml` file as shown below: ```yaml - codegen-xeon-ui-server: + codegen-gaudi-ui-server: image: opea/codegen-ui:latest ... ports: @@ -157,6 +167,20 @@ To access the frontend, open the following URL in your browser: `http://{host_ip ![project-screenshot](../../assets/img/codeGen_ui_init.jpg) +## 🚀 Launch the React Based UI + +To access the frontend, open the following URL in your browser: `http://{host_ip}:5174`. By default, the UI runs on port 5174 internally. If you prefer to use a different host port to access the frontend, you can modify the port mapping in the `docker_compose.yaml` file as shown below: + +```yaml + codegen-gaudi-react-ui-server: + image: opea/codegen-react-ui:latest + ... + ports: + - "80:5174" +``` + +![project-screenshot](../../assets/img/codegen_react.png) + ## Install Copilot VSCode extension from Plugin Marketplace as the frontend In addition to the Svelte UI, users can also install the Copilot VSCode extension from the Plugin Marketplace as the frontend. diff --git a/CodeGen/docker/gaudi/docker_compose.yaml b/CodeGen/docker/gaudi/docker_compose.yaml index fe194822e..965bbea97 100644 --- a/CodeGen/docker/gaudi/docker_compose.yaml +++ b/CodeGen/docker/gaudi/docker_compose.yaml @@ -72,6 +72,19 @@ services: ipc: host restart: always + codegen-gaudi-react-ui-server: + image: opea/codegen-react-ui:latest + container_name: codegen-gaudi-react-ui-server + depends_on: + - codegen-gaudi-backend-server + ports: + - "5174:80" + build: + args: + - BACKEND_SERVICE_ENDPOINT=${BACKEND_SERVICE_ENDPOINT} + ipc: host + restart: always + networks: default: driver: bridge diff --git a/CodeGen/docker/ui/docker/Dockerfile.react b/CodeGen/docker/ui/docker/Dockerfile.react new file mode 100644 index 000000000..04bb59fa1 --- /dev/null +++ b/CodeGen/docker/ui/docker/Dockerfile.react @@ -0,0 +1,24 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Use node 20.11.1 as the base image +FROM node:20.11.1 as vite-app + +COPY . /usr/app +WORKDIR /usr/app/react + +ARG BACKEND_SERVICE_ENDPOINT +ENV VITE_CODE_GEN_URL=$BACKEND_SERVICE_ENDPOINT + +RUN ["npm", "install"] +RUN ["npm", "run", "build"] + + +FROM nginx:alpine +EXPOSE 80 + + +COPY --from=vite-app /usr/app/react/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=vite-app /usr/app/react/dist /usr/share/nginx/html + +ENTRYPOINT ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/CodeGen/docker/ui/react/.env b/CodeGen/docker/ui/react/.env new file mode 100644 index 000000000..c5a7e3cad --- /dev/null +++ b/CodeGen/docker/ui/react/.env @@ -0,0 +1 @@ +VITE_CODE_GEN_URL=http://ip_address:7778/v1/codegen \ No newline at end of file diff --git a/CodeGen/docker/ui/react/.eslintrc.cjs b/CodeGen/docker/ui/react/.eslintrc.cjs new file mode 100644 index 000000000..78174f683 --- /dev/null +++ b/CodeGen/docker/ui/react/.eslintrc.cjs @@ -0,0 +1,11 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, +}; diff --git a/CodeGen/docker/ui/react/.gitignore b/CodeGen/docker/ui/react/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/CodeGen/docker/ui/react/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/CodeGen/docker/ui/react/README.md b/CodeGen/docker/ui/react/README.md new file mode 100644 index 000000000..5a9b07287 --- /dev/null +++ b/CodeGen/docker/ui/react/README.md @@ -0,0 +1,25 @@ +

Code Gen

+ +### 📸 Project Screenshots + +![project-screenshot](../../../assets/img/codegen_ui_react.png) + +

🧐 Features

+ +Here're some of the project's features: + +- Generate code: generate the corresponding code based on the current user's input. + +

🛠️ Get it Running:

+ +1. Clone the repo. + +2. cd command to the current folder. + +3. Modify the required .env variables. + ``` + VITE_CODE_GEN_URL = '' + ``` +4. Execute `npm install` to install the corresponding dependencies. + +5. Execute `npm run dev` in both environments diff --git a/CodeGen/docker/ui/react/index.html b/CodeGen/docker/ui/react/index.html new file mode 100644 index 000000000..fbe87e0fd --- /dev/null +++ b/CodeGen/docker/ui/react/index.html @@ -0,0 +1,18 @@ + + + + + + + + + Conversations UI + + +
+ + + diff --git a/CodeGen/docker/ui/react/nginx.conf b/CodeGen/docker/ui/react/nginx.conf new file mode 100644 index 000000000..00433fcda --- /dev/null +++ b/CodeGen/docker/ui/react/nginx.conf @@ -0,0 +1,20 @@ +server { + listen 80; + + gzip on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types font/woff2 text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/svg+xml application/octet-stream; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + + location ~* \.(gif|jpe?g|png|webp|ico|svg|css|js|mp4|woff2)$ { + expires 1d; + } + } +} \ No newline at end of file diff --git a/CodeGen/docker/ui/react/package.json b/CodeGen/docker/ui/react/package.json new file mode 100644 index 000000000..4b2097ece --- /dev/null +++ b/CodeGen/docker/ui/react/package.json @@ -0,0 +1,51 @@ +{ + "name": "ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "test": "vitest" + }, + "dependencies": { + "@mantine/core": "^7.10.0", + "@mantine/hooks": "^7.10.0", + "@mantine/notifications": "^7.10.2", + "@microsoft/fetch-event-source": "^2.0.1", + "@reduxjs/toolkit": "^2.2.5", + "@tabler/icons-react": "^3.5.0", + "axios": "^1.7.2", + "luxon": "^3.4.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", + "react-syntax-highlighter": "^15.5.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0" + }, + "devDependencies": { + "@testing-library/react": "^16.0.0", + "@types/luxon": "^3.4.2", + "@types/node": "^20.12.12", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@types/react-syntax-highlighter": "^15.5.13", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "jsdom": "^24.1.0", + "postcss": "^8.4.38", + "postcss-preset-mantine": "^1.15.0", + "postcss-simple-vars": "^7.0.1", + "sass": "1.64.2", + "typescript": "^5.2.2", + "vite": "^5.2.13", + "vitest": "^1.6.0" + } +} diff --git a/CodeGen/docker/ui/react/postcss.config.cjs b/CodeGen/docker/ui/react/postcss.config.cjs new file mode 100644 index 000000000..e817f567b --- /dev/null +++ b/CodeGen/docker/ui/react/postcss.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; diff --git a/CodeGen/docker/ui/react/src/App.scss b/CodeGen/docker/ui/react/src/App.scss new file mode 100644 index 000000000..187764a17 --- /dev/null +++ b/CodeGen/docker/ui/react/src/App.scss @@ -0,0 +1,42 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +@import "./styles/styles"; + +.root { + @include flex(row, nowrap, flex-start, flex-start); +} + +.layout-wrapper { + @include absolutes; + + display: grid; + + width: 100%; + height: 100%; + + grid-template-columns: 80px auto; + grid-template-rows: 1fr; +} + +/* ===== Scrollbar CSS ===== */ +/* Firefox */ +* { + scrollbar-width: thin; + scrollbar-color: #d6d6d6 #ffffff; +} + +/* Chrome, Edge, and Safari */ +*::-webkit-scrollbar { + width: 8px; +} + +*::-webkit-scrollbar-track { + background: #ffffff; +} + +*::-webkit-scrollbar-thumb { + background-color: #d6d6d6; + border-radius: 16px; + border: 4px double #dedede; +} diff --git a/CodeGen/docker/ui/react/src/App.tsx b/CodeGen/docker/ui/react/src/App.tsx new file mode 100644 index 000000000..90f3cfb8a --- /dev/null +++ b/CodeGen/docker/ui/react/src/App.tsx @@ -0,0 +1,32 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +import "./App.scss" +import { MantineProvider } from "@mantine/core" +import '@mantine/notifications/styles.css'; +import { SideNavbar, SidebarNavList } from "./components/sidebar/sidebar" +import { IconMessages } from "@tabler/icons-react" +import { Notifications } from '@mantine/notifications'; +import CodeGen from "./components/CodeGen/CodeGen"; + +const title = "Code Gen" +const navList: SidebarNavList = [ + { icon: IconMessages, label: title } +] + +function App() { + + return ( + + +
+ +
+ +
+
+
+ ) +} + +export default App diff --git a/CodeGen/docker/ui/react/src/__tests__/util.test.ts b/CodeGen/docker/ui/react/src/__tests__/util.test.ts new file mode 100644 index 000000000..e67ba2c86 --- /dev/null +++ b/CodeGen/docker/ui/react/src/__tests__/util.test.ts @@ -0,0 +1,14 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, test } from "vitest"; +import { getCurrentTimeStamp, uuidv4 } from "../common/util"; + +describe("unit tests", () => { + test("check UUID is of length 36", () => { + expect(uuidv4()).toHaveLength(36); + }); + test("check TimeStamp generated is of unix", () => { + expect(getCurrentTimeStamp()).toBe(Math.floor(Date.now() / 1000)); + }); +}); diff --git a/CodeGen/docker/ui/react/src/assets/opea-icon-black.svg b/CodeGen/docker/ui/react/src/assets/opea-icon-black.svg new file mode 100644 index 000000000..5c96dc762 --- /dev/null +++ b/CodeGen/docker/ui/react/src/assets/opea-icon-black.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeGen/docker/ui/react/src/assets/opea-icon-color.svg b/CodeGen/docker/ui/react/src/assets/opea-icon-color.svg new file mode 100644 index 000000000..790151171 --- /dev/null +++ b/CodeGen/docker/ui/react/src/assets/opea-icon-color.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeGen/docker/ui/react/src/common/client.ts b/CodeGen/docker/ui/react/src/common/client.ts new file mode 100644 index 000000000..7512f73e3 --- /dev/null +++ b/CodeGen/docker/ui/react/src/common/client.ts @@ -0,0 +1,8 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +import axios from "axios"; + +//add iterceptors to add any request headers + +export default axios; diff --git a/CodeGen/docker/ui/react/src/common/util.ts b/CodeGen/docker/ui/react/src/common/util.ts new file mode 100644 index 000000000..df65b2d8e --- /dev/null +++ b/CodeGen/docker/ui/react/src/common/util.ts @@ -0,0 +1,12 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +export const getCurrentTimeStamp = () => { + return Math.floor(Date.now() / 1000); +}; + +export const uuidv4 = () => { + return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => + (+c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))).toString(16), + ); +}; diff --git a/CodeGen/docker/ui/react/src/components/CodeGen/CodeGen.tsx b/CodeGen/docker/ui/react/src/components/CodeGen/CodeGen.tsx new file mode 100644 index 000000000..8d35d89ab --- /dev/null +++ b/CodeGen/docker/ui/react/src/components/CodeGen/CodeGen.tsx @@ -0,0 +1,135 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +import { KeyboardEventHandler, SyntheticEvent, useEffect, useRef, useState } from 'react' +import styleClasses from "./codeGen.module.scss" +import { ActionIcon, Textarea, Title, rem } from '@mantine/core' +import { IconArrowRight } from '@tabler/icons-react' +import { ConversationMessage } from '../Message/conversationMessage' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { CODE_GEN_URL } from '../../config' + + + +const CodeGen = () => { + const [prompt, setPrompt] = useState("") + const [submittedPrompt, setSubmittedPrompt] = useState("") + const [response,setResponse] = useState(""); + const promptInputRef = useRef(null) + const scrollViewport = useRef(null) + + const toSend = "Enter" + + const handleSubmit = async () => { + setResponse("") + setSubmittedPrompt(prompt) + const body = { + messages:prompt + } + fetchEventSource(CODE_GEN_URL, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Accept":"*/*" + }, + body: JSON.stringify(body), + openWhenHidden: true, + async onopen(response) { + if (response.ok) { + return; + } else if (response.status >= 400 && response.status < 500 && response.status !== 429) { + const e = await response.json(); + console.log(e); + throw Error(e.error.message); + } else { + console.log("error", response); + } + }, + onmessage(msg) { + if (msg?.data != "[DONE]") { + try { + const match = msg.data.match(/b'([^']*)'/); + if (match && match[1] != "
") { + const extractedText = match[1].replace(/\\n/g, "\n"); + setResponse(prev=>prev+extractedText); + } + } catch (e) { + console.log("something wrong in msg", e); + throw e; + } + } + }, + onerror(err) { + console.log("error", err); + setResponse("") + throw err; + }, + onclose() { + setPrompt("") + }, + }); + + } + + const scrollToBottom = () => { + scrollViewport.current!.scrollTo({ top: scrollViewport.current!.scrollHeight }) + } + + useEffect(() => { + scrollToBottom() + }, [response]) + + const handleKeyDown: KeyboardEventHandler = (event) => { + if (!event.shiftKey && event.key === toSend) { + handleSubmit() + setTimeout(() => { + setPrompt("") + }, 1) + } + } + + const handleChange = (event: SyntheticEvent) => { + event.preventDefault() + setPrompt((event.target as HTMLTextAreaElement).value) + } + return ( +
+
+
+
+ CodeGen +
+ +
+ {submittedPrompt && ( + + )} + {response && ( + + )} +
+ +
+