From 390adbb40104de63cf1e0c13982300edb1b265d1 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Wed, 20 Nov 2024 14:52:55 -0800 Subject: [PATCH 01/11] Adding AIConversation docs --- cspell.json | 3 +- package.json | 1 + public/images/user.jpg | Bin 0 -> 40424 bytes src/components/UIWrapper/UWrapper.tsx | 29 ++ src/components/UIWrapper/index.ts | 1 + src/directory/directory.mjs | 3 + .../ai-conversation/constants.tsx | 64 +++ .../ai/conversation/ai-conversation/index.mdx | 372 ++++++++++++++++++ yarn.lock | 46 ++- 9 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 public/images/user.jpg create mode 100644 src/components/UIWrapper/UWrapper.tsx create mode 100644 src/components/UIWrapper/index.ts create mode 100644 src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx create mode 100644 src/pages/[platform]/ai/conversation/ai-conversation/index.mdx diff --git a/cspell.json b/cspell.json index abb83dd527d..3bcd96969a0 100644 --- a/cspell.json +++ b/cspell.json @@ -1614,7 +1614,8 @@ "ampx", "autodetection", "jamba", - "knowledgebases" + "knowledgebases", + "rehype" ], "flagWords": ["hte", "full-stack", "Full-stack", "Full-Stack", "sudo"], "patterns": [ diff --git a/package.json b/package.json index bb353d977c0..67ab2821de9 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@aws-amplify/amplify-cli-core": "^4.3.9", "@aws-amplify/ui-react": "^6.3.1", + "@aws-amplify/ui-react-ai": "^1.0.0", "@docsearch/react": "3", "ajv": "^8.16.0", "aws-amplify": "^6.0.9", diff --git a/public/images/user.jpg b/public/images/user.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a4e7612efcbd33d81ae132d172f9c12a5a4e4bde GIT binary patch literal 40424 zcmbq)Wl$VZ)9&H~4em~G5AG7&-GaNjy9c-61b0{@xVr^k76|UnLV!g=kjwktx>fh@ zS6@$6|Clp#x~HbQPxpEHy#0RL0en=Dk(U9$zyJU+?-$^09UuijfP;sJheLR02nYy> zNNC7N?}Ukp`T-3K6B`=~6AKFmpOgRxmlzKVi;#+tn2d~qf&zztnwFZJmXw@={6CYx zyuXTsh=hTRj6sfzg-iZFrndnA7BVaV761oB4S>ahfy07%8v&5L>xlsKPJsVM2(WPQ zh)4h!PTE>Zn%=zAAe8Qc4)3O9co z`6~}4U+QD=S$U)@cYfc;{GKnQiU(;O=B> zRBT41{`=n&j6-KO^0K1uug4ge^=|DhRt+AIlama|WN*Ab&f3p-MWD)=6)oh;0p~@3 zxiR;@jNWD#YK(5znM>J!8n$IK?BzT#TnM>-iaX=g%A zM)JSa%kG9-Oy4So3n#V;fp!EnT|bIrG(glH5klVM^a4-T%>^Z=Z3}m{5MV`$guBmr zGa8{PwCO_HhQYR+>bzFpf+X|+E0s2JF8IlTbxNA^ zx5OngPf;z>2nVN1^{9YI!u{!g*

@k3pFkQdVJV=OnFXzzLhgv#mY(40%LJ@uVp zh1aCX!uxAG7zj$=snTmAF!64w6Sf& zX{i{7WZS1-LSi>Wdc5(cc4g$Yll}!o!6;>IVA3>+oH@0GY1JrZ(X$*co-pFZXC?!` z#)rYg5)*>qk%h;AnNKM};rdv|V}X=@CY9u^KlF^p?YfMyqG6lvcpOhoSsdB&G{GjL zT{QmAyHfr(r-~&tGqTEH+LmS}9o|A)rVkSuL88#RE7iX8qK!>^7SM)VP7;qfO8$M# zmC{$e4IDq=K1$5zR$;V4gAO;Y_4QHDX(^Y~u)nIUQg(?H~d8TI7=_jhdG0DE(9fXn5M0D)Upq^|h*rGFYMe=7yw z%{tMfmCx$bNZIkoq}uf78vvjQo+*5UEvWtS!^p@;38TKgzUY6$Fk^6z${CJQJ}XjW z_IQAcE#2gC+x|8|8#u+)mzIV)e7Iq@zL)=1_A?Ss4qiGL^$pOi>CwhWCdA427OCl6 zT8W;W8i;?ErKex3>(7Gg;Q8_x;f}pOoN(AQ2WlAurqh=IKVKC_tfwm*g-;P<&aQGt z2cBYTF&DI?LA3%^f29_pc~&Z%;`G1yPJpNM^C$IA&lBfV_4x2g z#)#V>$=Q1S9w$A&RylmPE2qIacMcRsWP)5oyE9t3mrh@Gw zh+6g*Z>r~#|Dv)H{?4%dETzUJW87b6I64nIk=^lW0>v- z%A>g}txNqv`pe1HFynr*KK^yp2M^mraZ-2A*Ci(dYOYebMN8DcCIyZx_5IVmIOA4J z=ZiIIid&t;*51va6GuJ=kHw*T;m5WcW?oF@*$=fw!$oOZXBMXDt~-ts>IoO$oLXDn4s-jJO*|7zs_gj!RtcLMxR7^PrW zS64RKySUJoNYg!1uOpBx4o-o?g0X3Y zaS|Bj(jhv)x@X2!cnR%BUx(0G=AuTP=eq+z^x2-@I{fF4{bmLPx*H@xDCRNK&%qfv z791RKw6G4Pu&>!Oscgu$>4Yu_DgZw&^sO(mu5bVz?s6nXvI!Py;+v88_^!@+iJ#ro(Y^8h>JqmsmV-=yL+W8g+b>kC-2E_&(wx4bH06V7dVW@;!`4JUz~}benJCM8*zwX#nz}NJ&fDZk{_JYc`Cwbl68UOH@sB4ENSaKB zPZ2Z!*mcxdt}t#*#1AP5(5|ff6fm3eCqLcIbe!<&ke_m1^m_n*>%XZfZ>MP2+25}< zG@yr&sB?51RWW9T(X2(x^zkb_r8PW>%c9n#e--L&R(ePY|0oPT|6$>>)m3lz$}K%@ za+x>S{HvGqJ*bzVa7YflwXGO6VJag%zw?%+x9cE2Sic;xxEsFL;ik$IdzxXoBaEvtJb~( z9l3JtFP9Fk0xh;gzJB*eh6d{S(64oim1|!LPN_jzeMN@f#fF&pz`9HG1`Yk|UhKYe}eroG~faVI6}q%dv8F z;qYdUZ445TS`rYiKS#p?*VJVNNCTEb?7XR6vSDOcTBm$*(A`UYg6(}*b!95c%PiRh z=zH~9DdkM;@2%JvGmub#? z?>gqau{@8GbFuh1DN?R9;Fx#%S#WFbsgL(cN>jKJCt2)XjR~&5=Q$alYqwY)N~Y$O zCcXCuW=TpX0WR? zLZmLWRfHc)aOahZr-`<$AWu)Nhktqg6G{U)?!}zYK_W4PPf;O`sr>7++Pr#y=D*jY z;>l|ZWq3$ph(E+*b)d9VNg}GjfiY|EuJSNR3=AfEFzF2(WY^j56t%;A1MqP9$Nha#rso|i3KF2#vRydl zb9mCS^IwsA&%8Ku|CXKeUHzTAGOjQ74VX$^&SFFte_~SU18MEb^AMYS)1i6hZ+=>a zWv@p*vNtg5yy6knU`|jTeWf1#YeCT~8}iLPAEJ$y$O;NMB6f567#s-Rh#4gw`4QqZ zOJ>i~>k+s*vZLeJUYmqrC;7)bF$UM@va9PxVjA)fjC3YcF?U<|P}9`qa?${Q8Q>-< zB~LxEwmEnS`3DkrNmwF`^lr2s?$CFm^d4UptTYBLbs zBjQn=K`BdE(c>3u6&uu%DdcNlXQ`bSkS>|5X&Vz3SoXp!w(MW4(-}HS`;9snB}^P? z{!dVC2|0^{HP;PasY!70g5>da#C??ynPJ(Oa05+Yt2HI(zFQ?tV(jFM?*530a0Hoh zHCt`9@B9HJsdcf`OclVim@YTrBY1DJC2}0GMIDIqYl15o!+k$0<^BmwN_a#Y0PfB& z#PYZ)Waa2c6Ys0DE4m0?{6wcJ>CRj!*|rlgYunQXOQ;4p%2KR%_A>UE&;D#dd`9`02~~4kvXAQ|4OV_GLD|>ON~OXS2Ce9b0VfYb zWhtKr>Vt*#7sxVlT<9HZn#Nlt*u5pl-Gf_Amn>QrN|{B8T~2w7f@F?%;{i2&13L2| z!-X`JP3wvzf9dBkLhgvWptBi%q_Nx>b;CDL+9>E6w$8klh^peMZU&+So6^Mwt7pVf zp>d!?DQOOYuLZ6_9hKZCoGq(0P@P&IJ3-cA-i?H0paU26rg$3uCrc%)xREZtTJ9ig zRhT#_*GfChv!Fp>dgW1)kubIBk$Tioqal+Uu#oTB_0+E`pymxgVnhrDmEuEUX(LyF0IvpCN6x3`?!;A}Stovl`IOF#gD&v3pmGa*J>^eV0A=)ou zRpg?m5waJ88FEB{6pqW6Nc8L$<2zfs**uN%kPfh4AG*WYhxV>;w}&ekh=0cWh5$}- zuTo5%Wo#q8V}NsMs0X9QGs8JD45M{gP@)bGghFS&;?JFkpq#4d@15mii)9>zU7FNR zu_fuJIhppe;DYAHo(!5yZ(0ReO3lYX)rDKaPO~*Z^{%YG4V}WeXF@RK=UUJO0SXd| zFOPcviF0X_d$S!Vh5uJVTpTO6?(&|+`U|7|ZHRh_*npSZ4f&YY8{nWUk@ry4*V3db z7crMACgs6H(P%3n#6K;wXJ@h2<|@<{l`!6?x^n;D;=)>(>bP@95%P(^l}ZD71{Ia6 zy_dQAsW=*2Q1uAXTA|)}D}OPPJ0oByS_HfpSN5cNHX$M%&?zzsC#6WD5$UAj+f;Qp zO1nA0&&Hr+Q$o!gNxz)N?lk@){c=jL*8bY0gp`T#Sy9hTJtTI$!3Q_Jb{*q_0oFCv zPLk)oI7Yu6YA><8{{~nsg5BeU7}qdBi((AP3(w^Q*ZtWair{v&yc>M)Yt|h!p5T<< z07Uu!KuzkJz4=lOv~>dr#>78GW>3yk%e1VpzoO7Fg$;zziWbi=g$T=urZkmo5( zCQi&cP-szZOTfFMEBM1-wuZ@2ZPDtvI{j@6^KZ2x(sCJkXL(|aA%*FT%*HELTte!M zM2nX#_?SD`OD8JpDVhmrxlXK?d>G7#62ZxKxdASdl$LBsr>-cy)|OIw>6R6WHH~#}!5twnL zKxg~CIgEJW>}cI{(Qpw!NC2LBWwU+YJjbN|G786g4bg&dS;r>FV)(d4oSLY5T`5Zi zf?@%;7>{*|`lrmjqNiEI+k^)kKr<22RZhbN-DD+)sY98GFTc#DpB z?(ix-9X5XpV|^v92Dm_rIkPJhE4^-0$9XJIwb?Nv=ujTe0^%l}R?C*-ePh`nY)*yF zv}iHpThUEUBudK6H3h;f*2~sOU!FX|Meo||+#dC4@5TCC)m|y>;vK-Xez^M2nrw0p zh;2Yp*Lu${XVs;Pkdp3qA)oQ5K zG;bYkM^oVlR>Y*qw3v#r#Wvm{dPw4zueNXH&lexy9_mVq>DXB;o`+%sEtkE$R8p-erf^}ty)au*0CNIZ7rgL> z?ddT!6mVw$^*t{ulh^BqVYq4TF5Psn>$}Nh&X7G7u>3eD`WI&>SgMq!PI{jr$cOZc znXA7)_?)Wvv`TO??Kna)&dhPmD>(ZUXrgHNNP~Znj~%i6lf1kA4KTD8E=6^1_8o`h z-(HJz^itxsqVf8%N6}U(WJddyX&j?<@mhsuWS;yDp!yP58RgcPB>OAj#Xk+_rg-A0 zS458U@LMi+W@^HYhYGL@Q{kYwa#idNfHe^y9F5H-jgq@qaf70X`fkL_{xDX_CkHd7?LFBz8-WnB*h5v$=~ z-KN>?Mw}{q^;g4{#Zm}pWsrQWw|jzWHWrshB*`~Ocl}!&ZVix*LPsXRJBgAl7ZlP} zH3$)E6@@HVTD+XT=nzRWr@|egiG` zSgic{&irtEjBfgLY(9I z6|Y&J1>xJ~8z55*RAbQhKu)UQZoh7i_GHOEX24mvR`LpmL&3n$Gs&oaHYS{^zW^;M zPuM|ARzKIMX!=?>leD27Y8W+VpX{CobHKh8m9k)F{0qDs`3E7usS+R)un^g1BQ&s= zuNO+$IL6qM`^ii`k~=;GVFM%@!_e&(UVB(yy= zFK$}D(gsnJq)P`Yb0Ky3vC7~+X=dJkmh2o(@?BSNnNeZX+fy#OVYn~!t`oP`K(JmH z^i@-pOX7N7vDY&glimB@0J+F|fJ#b?P zYB5c{<2kITrES95i~W$HcR)&tr9GDv{SGc*t$x*KTMtMS70?c;WLc%?D3Pz&JloWE zO);EEo${iRM;^ePMd`dT4Hc!6S&m#Y>;b2e*Fz z=_aY8kczgh@}WWeZ`P{FC)3S;S`&1vqexq*uFCQ)DdDbm^S#8|!xDR2mFFGO4{VAV$rWcgW9_v+nWUW=_I`QdUx+t0$joNpTNs;%tcx+8FgP;bs_!?UY%F z5u+j$HAlI7P}JygsgfcT=5RsjGwj0ZrYz*l^LCAP`XvLd#1GuRxr=NYi@lD4zRX(; zjl~wN-WT3wmw1C!@XX%Q?--!Oj6Wk)EON%oPkJGpz~ zNVzhBEerl>$p$t$6JxUW!53E8wzGKS554?_DgP49Sv^zZu4T*6TELnu>dJ88!5%1-k?VWs$ik+)212zU*-fDcO zz+qY4&-E)e>d^@zV=hi7E^!;grPnI7Tf-C>L-^R8H{mL+`(AX8VEY%>Bu%=@wV#R` zuK7)1=yG=}-nVD>IIGA(w1t4dgcjEo9tkil?#il*&Jbr)?7AWvcddM4XO=pB?M2a; zN3(hse~Z~2(d+O|$X#V0rQvCi{DNT32%dA%w>f;VY6q?$0|r5yN3W!2ydN*SH(h>l z3=iY>@s)F61Dy;HE05|?fBNL%Ybk&?*7lV`^jf#HrVRLph4RRA4~{NI!IFH6%y78S z68W)efDBW2V{b>bf%5st8HTSgc%dev)b?~ni+1NYPYU$D zs-}`szI#!4IihS-*c<4D8&7I!8MakJFMMy}ID$=^lz5oh4iI4=Q16Ue((l+9^?(fG zU8&Ai?9_h7rxeS6m=OSD$-1GPGW%L|VayY)DHA!luuVsE z-mmZKnlt_?>$nT7D>k$eeWX49w;XYeSyy^W!bsf=CRSGQP53xMLWTQ4zup`81`tj< z{E^+<$)fk__;1A-IY8if`m&>-_G7@}w;eFY!9yiYp#O?$^RyZAcB@j;lzc)Q_Rj#~ zAC?CS$lUXZS;cg`KNxE^5)vD+nX$vIGvzBSGE=8;9(WpBYVH2~d_CbsOTDNw9!W+1 zr#*9$jcA^ip9H>&v-MOR6{LLvqfz0G`0)6gD4zRSr2G)YJ5J`#r?PNzzzNQutFX2#)VTw+D=N_sx>* zk}BKceleOQ`02|VoCL@yDowS*YxQNrrI5VXmGeN>;oOhG`HQA2={(8>nS6KTh~F!6 zV0up#(duA=i^u}~rur+Ba{ns%pN>3Tp!Zy_r^UjY=%n{%l)UCviMYL7tvNvfqd@%( zZLXww>`kt<%uBV>mVvcqYMA6l>J!IaT5tPwTUWb82^A@uRE4PCL_GxcN9r7ptuCAt zhe;tfc^GVqZCsO*Edduc7Gnvb4)Ko#>oH;wN$cHcCc(_ujP&qICfVZRTAA$fG3hI% zBqu%%&Ix3hiOtj84!h|FitLE6t!fLcWVO@BPYEua-$HjD{w>TfJp|QcIHqwpM7!A) zlyT7!cK*PdZWq>=gvcc+GL?bf51M$ce0Bu6}s5J&Jgr3OrsEmEz+;_XY?17Gw^Au4L58& zEnJ~bnrepY3WwjF%st}BXc=)srZTv;4Q{p`$JFr#yQ1VLX|XO)!PnR>Bk5|L|IzmVKV; z^GuV(1qv6Y316V*!UxA8`oLdvSIW`Vg1t>``NdF2;z&aCp4HcJYtfit(9#)KuA7h+ zuAI*M>S|_4Rz1QEczSocU%5I#w#4M<#t}2zi3vxUgcJ&h{OAnC4qpC*^KeC=u!`@w zYVS_R6Ob!T-HXNm<5v&A61tN2tq399Vcrg=**HQDrE=6GKHm2YP%``Jb^LWHP4V?} z(7EAb^Bdq{y@jka5jQw=S@M{xP2?PCdlc|#@KmEO1A9F`h)JDN4JpA*j*a<86t~-z zcy@(joVnCpc^c;^$9OOD7sH`cL-@)d{Gu)x>v-gJjB*TtKgw((vBA!E(5(9Tf)n9hIwIk#T3AGQ* ze@@{pv@y0!MJN%A6^aghPm)71%tgnIadlDBny@rgOiv3yIdBzW(QIu>G*{{y4bDiLWTf9Q%Nh`agHygHCSM;b{&kjo} zoCD^*dNxJs%pl$73l$_`-7bqQ>{F+jXuQs4_NkEK=8&JYE7CMmze|Agw+|8#>pZrn z=-#&VmU`=00r^_Iv)Qw#U-BBF<=v4JkEBJi+j-Pho@DmLvs>6{Aa}D4E{m-$Ba#&= zhNRjaz-ermOIHjI4~??b0=decIg45BcnO5?Q#3n;j>`>Y+aFqnNIqrw9XHjvGpAa) z*Cq}maRg8(^m+oHOF&wt@X6tD;g!`-Ny`2hjDT>V6*%#jr7(Ey z?On${{2Il;Iu842VykIePQ`dtNb`=VMis8R#}G(Bcl1T&^b zoIkyrwN&v^dGtWVXNUs)Jp65Szo)KE9e#4JPq!*wW_j|w9(g?G|Q&Ulgta%2GbcSCP3Oef5PzDHhJT%Df<$Q|ruk)L; z|Bn-Dz!1**mNOP1!I|pXa3`#ka)+wzegAM@=H8PFIR=F?_TxA!o)hPf^J8Zac+bDf(DA569EsiQH0Y2Ob0(L3@dgnlOd=4nZP#qzMJ zD;avYKN^^TF(9-9N}DgLk;y9Wok>A;=L{Rw>6+6rC9`xaCB^I|cQW5P8W}75e1DBu z392&k;n&|wXw!N%IjtH;&$TLC`NGN(>ujJ;F;T~179 zWA&OMROlV2f#?KbJ`?D=rXldN@W0pK{!29i;5?c1qFJZ?=%Xt(W18KUcSy=$MpW?J z6xE6RlkK7HNORTG{p*tdDY#?V%TR<^Aa5*rhI5>pT-v(-ng(8H8}D*AMhJiHqu48K zEy^a*K|9Ik@;h>H3wNplY&XM>#Wg9Y8q0_0lv^#za;Z?MllKRY2b~|2cukdK@Lr-r z64iW30=ZREvhj-mL{wP%UDQqmcqvu>Dc)LXX}V zFQeJ4FCH0@ooVtf2PW>H;^sk&BQ#*}JKA4fUGzaH+7*ET)-F=az{@aBicBIQ*C78! zXpzZ7Q8Nh)xL5JxE_c+VdO3X$F4Ccw7rD?t;C+R5Hz=c7I>9ewh{7LAswthsX|9~-28bcph@v>x(o4>*-!xd~=IEOy zxUb=Mgfs6pD9s(>cy8I{ouX&YavJ1YoW|}r3^D|m*%$CYh5u}5h~9;y;bpp zF4vtyS%X%&*{adrYyb{d5GOWKd214x^MxtTODmMu4Ko3O%H%z9~3tdxt=MUY_Ackp8ib- z$u8+|uiS7#4Z8#0|DT!zbGv-_{?s_0sAK_&6Rk5T*p2ru5!<$Y4&5Jcp~r=7QnBEc zRA&|o4cf>P+O@VVt=?}$6RgP_m=mUjQklk_1J|2osT4y00QFHS50{^8kx5g}$HSMb zgiu;?rbRohyc`nu{C%QJlC_9AQck8DLOHze$y3mmv-Lk$lEg{%?GXI;)m$q*VSlZ} zy5rt{8@)vvqLC_8w|ii#3x^0lEvh%!dNw0+6+) z{>S>ohY-LLo=52Y!Fzy);x_g-_1T6Tmm*1D%{AceRh2uzG$NU$8$+H}{Puo--^qnO z@C;Zi_lh)ch2q2&)=&14Ff^|dRnZFoP>j|evr-#e+ z3qIeMwQ)(;NYQ-HbVCf*F-uWxy07mH5Afqv%7eMs91cqS%&qaIsbwKU_pkhc=CaQf zETN^kFzxqeW@5Mh?Use7ncxlC*u2UH9cuz*iz z*kbqXyI+%muKeb&ZtwOsX6nUCp=b=IHvs6tO5w1#reH{r{i)}nT`;hgp0VwxvK(Gt zaKHnQ>-9!;w_ZK==Du!N&(-B~_=hXK1DW5yGt3~0NMKE5d1xgJ&$PX0;*|<>oLO*5 zCZLR4H1WO?&O9gKKx2$k&Z2k#q9_A@l&!%yI%v!Q^XL4MIWp4c4x(<8yR}9Afc@=z zh%QjFahbj&&nhY|DG*4{4Z@2a>H{5;*j~zwx^vJ_H}rB=Y@8e6RD}?>qIx)Y?^}L# zCSNK@Z{YC_B4YFs&ua>d0IAVVqB{Xkl= zp%i~2Wli(LW}Uv4Kt1uoWB+OyOwuP5D4kpSp2)R=xm|V=lE0=H*%`o)TnOyr{Jc`3 zV3kZ4iPTw^Zj>v}V~Z)vy)iXzZ;*RED^Zk2@#}AOoO{tx@oZ1JV%SYoP)(>;@^TtR zfGLPHu_r~eqE}-yJk-aZ!T64KL0*5KSE77%ZF3|2F+l$k*XPfC`;&J6?vxT>I$$PU1A=e9n!&*g-O_}~MdIHw^NcN!iK{C*cWs+DH$C_fkjgQNVt817#-iw{> zkjD1upZ0JS3w39V2P4ZnGaGW^)lTt>9|Eqcs4;ROQ zc@c10V4Z<-MVi`0h&0*Re)^-jQ>m=}4Tk)$q`eH5Z$%6m?);;Y^6luwe=E(qV~rv! zg#yeo%2zGE70xDlyVrKSB;vOBbi~Z0LaihEvL_WD?k1?!j6DT_CHQ0GJVjoPwv)Q9JB`;V+dArSBTunw)8zCxszLf;_m#T7xCdwe0 zAWAkVw?HxiqLEhO>a(NW3LKz{RouYO+RbU`8bhEPngeEGznvflinEZ%fKw-p9eaPt zE&Tx%+Lm^+)_!Ojx__+g-^+DTIZJ?zaKK8_n57UkqAXOKi03XTyqD6e_4PKVK7taLO=;B&w6-o1> z?jS`gvLSPxL#NIY5v$cKntQd*OJdOa;{04dFv@gOjkkHvYr%KTDnTi^6(?hK7p0#| z5;vE=Lvl#-$p(s+@@`^cHVNq6N%Jb7bp!f$jXp27c^DZcDx96d$)ZIZAc2oTZ3XW= zA2C8CodjwpFJIVJx$bpssDk4*bTqFcDzBhh9We@hiNcBw4B#pO^+WQ$mO^lqY^t_% zbyH8cqLY+Flo|6O7O9}WtF7mnq>+C73|YeUPOO>P@mK$aoR;5Zk$_NAS2yu3W*|Al znf1^T@Xs}>G8P^W?~_#^OKaOVA2n?#gm`;G1`-=#dU80rS4~;C}%F;xmFHd62t8BMLBbr z^>h8UR*qK&Eu4hwQJh9FZ2@cQG+&SZ6kueAky zm{JOLNb?>ueAUM5cJ~!_x9#NWFS4iVUQPxpifw#2?amWI=~{kQ?5z+f&+>4aiHM(Z zAP&nm;#R&Osp{JNuIgP`ONuK)iecxps11e3n)-IZzJ6ZM{o zM{LB2+e4kWqb)@&L{Cc9HnacO_w@78;QCafv{3F~3Ss&KF!LhTx4Kt{>QZ_;UL3Ly zNa5ZWRlZ87vW0P{p%aNp?)BuDnxF1vfX*nWAI8d7C%9y3+TC>qp$f@m{@p(9CueBn z2Ahc&Je^GR3;#SBMLwkCBsL|4J@7cSJ&LVpjDSQ7(fFZh!6A(eCH{&P+_UMRJmrOA zMiSEQcDC^|+kExx1D0W$^{vh=f4)a;3aWD>BJh)d%ebA#cZve69_?VY$qX;w-yv$0 zuS@qw<(AQ6*h{eF2N+{vlSQZPQ3dC6i-#7spVzC!`k1oPzX&6ZFCfyxw5Pua%$Nv% z-nH%q<8LQ)Sybr1YSOd40nAJ!458!7zi0NPice2oZg&{pzry|KnC1O+1>6K~VcIw; z9a+sYQHPv(*;xK`%_@k$OcP%rp|b4kO-q4-GurVoh4_} z58Ucq54Z10I<)klMRO=8`kCm2(2LM|#j93;_>-fd6vKeaE!Md~nKwTEG_1aOh1mB< zQtf)Z45x4a-bX+29Tea>8?_EiXO$mmn;juTv0wl}_oW;KJE+7<4V!J{sPg362`2X7 zR^2~|%biQ^N<%|BL?!*t&nPA|C0@vr+7J7g7Kj|9*bV4!v5N$>^u`bpWeJyJuh-nZ zl{srk4zA)mI4_S=C+i zwB2A#)uS*FsyM+{-|&sbV*e{w!u!YRs|$QkjnmIg*Pz}SD2L3~@iRNvMEpGAZ&FS~cND?kE0yT(^Ana7Yi4Bh zk>hzG*V21#@Ml@{vMlVzFHwqpK~W@+CKWR-<#V-0)rLPbOh_X(&7VVGv+I+|F_iXi zeliTSm^eKPm;FJ(7gJ&QyxtPTcD^s4si8f1NV140rl0j{EcgB9Rs9YrPWcxesE0iZ zH6b7unp;)C<^5}0^^SImAx3>|b>Htw>gnuv@F#ZDpaqVrbICisXVm7XeeV6uS9&D} zKa8NH0A14K6NuVoe)|Of*pNPxdC~FOHX5jK%ugIV~-7$xHwR(5dTdwO{!# z(*!bVxN33jC=JUsWvm#cr;d0CCOy>)?lRRI09%ms^PN>vJ)X5bghP((*R^5d<_3xZ z!#BqV!W|v2>!43Pi;fiml1&4nikS8G&A}zt?KE&5?ON^RN&q2?R$ZPvy~~e@mC0YV zAF4;(Fxdck{?ah0)q7A_{VeGXO-UFQ2hPH=B9D4n=!1`)BDe+ab)Lg#p3o3i1y$H$&qXGHz!wYe|F`>bpQDi}eFr_ef=qF@d$6S>nx+xr#Ry_uMs zWexerCnqt8LFMop;4wOLXqNA+s^IPuEz1HLS0i-XMcwc;dm&-ip~`M0*}*%_P$34B zq#B+$B8Ud{?$fM~yT_@4i_Wvjg5Y9xNwV%dWjiS6KCfy8C+dOz_Ps64>$UK-!|P|R zi-xQZd}0?bXNpz;AKv$|Z|Vi>Dl&|INWwu3Mf1ftv=pNd2}+!B-x;~2IfXC-0q4l1 z$|J(TdXvPw>LP^(IJ~o;`|!~M5Tp9DT;m)o%Fqwx&IEK-%(YvzK>E1h{4lL1q|Y+R z=wZn!K~dsMckw}jNU1qQ12@GrBfV0A^TD`nD>NRBQ1b7HXR;mNed~;FuLdfbK1`=` zKjhB*&ed3#zp3{%=UmEnk4`Mp~VI=^%Gf?4BAyaOZ|F>cS2cP+PJ;F(|h zfuzE;Gx+y?oi($=z;8Qqr3NN`Lf};m{))~|L6Hla!(aSJ5qjkPRz4N>Tx&))e;vPA zJ}?{bg9JJaiBpC7m`IOTaPx1sa&*kOLY@}m*NXA^D_53#Hv%+Z_IlAnZ9n$8{b$p; z(PQg7IwfcKAnW<4iwkbr3=QC0jUo;d5BSXcNbZa+qF7fTXF=nxPmdPVPNB36Tp;_f z*vpKu$u%M}`S;TLR&M*gRw~?Ge#Tg72Kt3_UQ7bFF@pe1|NpM>YgLJ%yN9f4>v$5k=SRv z)D* z@kEogr|kF-sDR}CI2&DZP^Aj17pj2M0a->&NPUnUge?20`~n9RW)QJ*wcvAZmT1nw z20^P#5<7Yu_~N9oH@{q$>KlcW`W^H50TREMff*gv$UuQ@ASlz(hT9EWOYccy^-7D} zeW)!6g9kruys<8Rce86Mpm!Waf~WP=NX!p7PLw3`vyoKMBgv8iR8mVWpqyhhgUlKW zVoBC=K%C_omTOKC+4$v~6JtBx@djwMSBIcR4ONk1nn5I?@rue!AZBzy_~pN~88Fq(bLNB6G9nacSo}0tw z&Tj3v#|wB&`vvalnikEjq><+4;)$B(iFr{B`!_%}89iw&{Kd~jgL;nr%r0L_+w0SaFJ>sYR=lF@QG`MHUp3Gp6aRNh0`EVV2 z;ZWitD=H#4>c2ppyBU21PipC=1%rz<6Tyrx(vB1Ep!1kq+}nCl6E}mlzE2+|>lJnZ!lDvTOrtrR)fX^Vf2n9`lgCfHtd;Jx>u*A+X zDM77D^aiNp%MONqPp`bw;#9`%lAbHA%%u*l1a+I?%uZ7BTzIS*weT^v4ovPR_+x3d zcLkyr{&i>-kaiI#`Tb7~$ZEalb=cmkoHMNf z^SjpD3<_lUHMnOpPM^jdc%k4hKEEy_>1G!a+t5UhJq>F(s_fn%FO73r;fQ>#P#$v$P{?EYLk zkIRyvm>TG!-nuJnFL}gDQa0GP7@xtB``>xYS0z2kLXWMMc{Qd#ax3gE&c)@aKjof_ z0tzMM+vUtU{y_Hmd}z8Kq{XgeE})U^KQqs6T!sf^EYp{zwr!u(x`EdG1cQsfOuUN^ zS8{^LV?1=K$I82yr?7FSsiW=av`O14OG*SQETd&P>jiEyP$S7Lx^23fAT9?+8e6%g zE+K`e9P7@+dw&muN`)^}3BRluni2>2TFrw9>WHo^Oh`~qx@-NqT0)^b2TcrYPB(gd z_DB28%?AqHnqJNgy{+KN0+R6S%mJCN4O>y?Pxn87U(l|r3Mu%>Th$1-n4EMWC*zbW z)rHf5-ALvpr{nfYut->1B z`TMz09!kCmktUU&nCj~4Ifk?!nLJr2!X}@<($sayiTxHTOx0wylka_R!&3!*PnxOd zXZnP0r#dNlB?6R(at-9thIUxpP6OrB+McpJr%?d2Oke?=P?akw_E&UO^OAj7nzCxz zV&C+vb12M~vt<2_fA2u_*urGv>T37WD3@^FSvbne8Ekz=G-IhSlhIDF(51S!KZR^M z^2_f`$!&29?3OZwa6uJg(~Oo^MZw0#!K)UdX>F@mG|{4&F`hUzy2onK4n}scbCL)9 zRLgnT#9*2@?rqQHm2w3l?+{Axx%p2boPlF!45x$XST(aqx7fendL6*bx=82$047bJ z?L+Rm3#RTInB>2+Z+!0`?N$g~0%4eb_2{@jIAQu6RV0V<&2&?w_)}><5)$^QUaw?il=fKS&nV@r434(wyKbVCwk%gLHui0EI2y)U57 z9KGJBDv)=~*ie3pS{=sI5L5~R3@VR7LNu7vw}{dLPt%$!By7-<+nQBN?GOlnggxjl!1Lhs^T>Tvl zrh~mTdmLA9Z8^oY;`nlpVd!XHuVF5qbv3a&wiD(bnRCIY_AWLE+b0M0s}8jo)HFCX zD4!DA;yJ+Nh3pPLYSQf()jKkuikiz=*+3F~jo{$qWcM|J>$-x^GZS&*6ZGc3j_b~t zvD9>?m^qSE9QnOF*Uw#1rCQm|Cx;sb %2Y`M+2l{q)a#Xuzc(n1i2f(i7eGRS>L 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 { + const { colorMode } = React.useContext(LayoutContext); + + return ( + + + {children} + + + ); +}; diff --git a/src/components/UIWrapper/index.ts b/src/components/UIWrapper/index.ts new file mode 100644 index 00000000000..984a3cb3738 --- /dev/null +++ b/src/components/UIWrapper/index.ts @@ -0,0 +1 @@ +export { UIWrapper } from './UWrapper'; diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index d6337968899..601e28c3ae1 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -753,6 +753,9 @@ export const directory = { { path: 'src/pages/[platform]/ai/conversation/index.mdx', children: [ + { + path: 'src/pages/[platform]/ai/conversation/ai-conversation/index.mdx' + }, { path: 'src/pages/[platform]/ai/conversation/history/index.mdx' }, diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx b/src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx new file mode 100644 index 00000000000..324a0c1a64b --- /dev/null +++ b/src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx @@ -0,0 +1,64 @@ +import { Avatar } from '@aws-amplify/ui-react'; +import { ConversationMessage } from '@aws-amplify/ui-react-ai'; +import { AmplifyLogo } from '@/components/GlobalNav/components/icons'; + +export const UserAvatar = () => { + return ; +}; + +export const AssistantAvatar = () => { + return ( + + + + ); +}; + +export const MESSAGES: ConversationMessage[] = [ + { + conversationId: 'foobar', + id: '1', + content: [{ text: 'Hello' }], + role: 'user' as const, + createdAt: new Date(2023, 4, 21, 15, 23).toISOString() + }, + { + conversationId: 'foobar', + id: '2', + content: [ + { + text: 'Hello! I am your virtual assistant how may I help you?' + } + ], + role: 'assistant' as const, + createdAt: new Date(2023, 4, 21, 15, 24).toISOString() + } +]; + +export const MESSAGES_RESPONSE_COMPONENTS: ConversationMessage[] = [ + { + conversationId: 'foobar', + id: '1', + content: [{ text: 'Whats the weather in San Jose?' }], + role: 'user' as const, + createdAt: new Date(2023, 4, 21, 15, 23).toISOString() + }, + { + conversationId: 'foobar', + id: '2', + content: [ + { + text: 'Let me get the weather for San Jose for you.' + }, + { + toolUse: { + name: 'AMPLIFY_UI_WeatherCard', + input: { city: 'San Jose' }, + toolUseId: '1234' + } + } + ], + role: 'assistant' as const, + createdAt: new Date(2023, 4, 21, 15, 24).toISOString() + } +]; diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx new file mode 100644 index 00000000000..c378c461ef0 --- /dev/null +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -0,0 +1,372 @@ +import { Card, Text } from '@aws-amplify/ui-react'; +import { AIConversation } from '@aws-amplify/ui-react-ai' +import { getCustomStaticPath } from "@/utils/getCustomStaticPath"; +import { UIWrapper } from '@/components/UIWrapper' +import { UserAvatar,AssistantAvatar, MESSAGES, MESSAGES_RESPONSE_COMPONENTS } from './constants' + +export const meta = { + title: "", + description: + "The AIConversation component is a customizable chat interface built for the Amplify AI kit", + platforms: [ + "nextjs", + "react", + ], +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta, + }, + }; +} + + + {}} +/> + +*Note: the example is a mocked component and not hooked up to a live backend* + +## Introduction + +The `` component is highly customizable to fit into any application. The component is built so that it works with the `useAIConversation` hook. The hook manages the state and lifecycle of the component. The component by itself is just a renderer for the conversation state, which the hook provides. The `` component requires some props: +* `messages` an array of the messages in the conversation +* `handleSendMessage` a handler that is called when a user message is sent. + +The `useAIConversation` hook provides these values and manages the messages state as user messages are sent and assistant responses are streamed back. + +```tsx title="Mock conversation" +import { AIConversation } from '@aws-amplify/ui-react-ai'; + +export default function Chat() { + return ( + {}} + /> + ) +} +``` + +The code above won't really do much, but if you wanted to play around with the component or visually test how it will look, you can do that passing in your own set of messages. + + +## Getting started + +Make sure to first follow our [getting started guide for the Amplify AI kit](/[platform]/ai/set-up-ai) to set up your Amplify AI backend. + +Conversations required a logged in user, so we recommend using the [``]([platform]/build-a-backend/auth/connect-your-frontend/using-the-authenticator/) component to easily add authentication flows to your app. + + + +```tsx title="src/App.tsx" +import { Amplify } from 'aws-amplify'; +import { generateClient } from "aws-amplify/api"; +import { Authenticator } from "@aws-amplify/ui-react"; +import { AIConversation, createAIHooks } from '@aws-amplify/ui-react-ai'; +import '@aws-amplify/ui-react/styles.css'; +import outputs from "../amplify_outputs.json"; +import { Schema } from "../amplify/data/resource"; + +Amplify.configure(outputs); + +const client = generateClient({ authMode: "userPool" }); +const { useAIConversation } = createAIHooks(client); + +export default function App() { + const [ + { + data: { messages }, + isLoading, + }, + handleSendMessage, + ] = useAIConversation('chat'); + // 'chat' is based on the key for the conversation route in your schema. + + return ( + + + + ); +} +``` + + + +## Formatting Markdown + +LLMs typically respond with markdown. The `` component does not have built-in markdown rendering, but does allow for you to pass in your own markdown renderer. This also lets you customize how the markdown is rendered with things like code syntax highlighting. + +```tsx +import ReactMarkdown from 'react-markdown'; + + {text} + }} +/> +``` + +Using ReactMarkdown with the `messageRenderer` prop also allows you to add any markdown customizations you'd like to use. For example you can add code syntax highlighting + +```tsx +import ReactMarkdown from 'react-markdown'; +import rehypeHighlight from 'rehype-highlight'; + + + ( + + {text} + + ) + }} +/> +``` + +## Rendering images + +The `` component will render images in the conversation history by default. You can also customize how images are rendered, similar to the text messageRenderer. + +```tsx +// Note: the image in a message comes in as a byte array +// you will need to convert this to base64 +function convertBufferToBase64( + buffer: ArrayBuffer, + format: 'png' | 'jpeg' | 'gif' | 'webp' +): string { + const base64string = Buffer.from(new Uint8Array(buffer)).toString('base64'); + return `data:image/${format};base64,${base64string}`; +} + + ( + + ), + }} +/> +``` + +## Welcome message + +You can have the `` component display a welcome message when a user starts a new conversation. + + + {}} + welcomeMessage={ + + I am your virtual assistant, ask me any questions you like! + + } +/> + + +```tsx + + I am your virtual assistant, ask me any questions you like! + + } +/> +``` + +The welcome message will disappear once a message has been sent. + +## Customizing the timestamp + +All messages have a timestamp associated with them that are displayed next to the username. To customize how the timestamp displays you can pass a custom text formatter function called `getMessageTimestampText` into the `displayText` property on the `` component. This function will receive a `Date` object as its argument and should return a string. + +The web has a really nice built-in date/time formatter you can use called [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). + + + {}} + displayText={{ + getMessageTimestampText: (date) => new Intl.DateTimeFormat('en-US', { + timeStyle: 'short', + hour12: true, + timeZone: 'UTC' + }).format(date) + }} +/> + + +```tsx + new Intl.DateTimeFormat('en-US', { + timeStyle: 'short', + hour12: true, + timeZone: 'UTC' + }).format(date) + }} +/> +``` + +You could also return an empty string if you wanted to hide the timestamps altogether. + +## Attachments + +Some of the newer LLMs like the Claude 3 family of models from Anthropic support multi-modal input, so you can send images in your message to the model and it can respond based on the messages. To enable this functionality in the component, there is an `allowAttachments` prop you can enable. + +There are some limitations on the filetype and size of the images attached. The file size for each file should be below 400kb when base64 encoded. Also the currently supported file types are: png, jpg, gif, and webp. + + + + {}} + allowAttachments +/> + + +```tsx + +``` + + +## Avatars + +You can customize the usernames and avatars used in the `AIConversation` component by using the `avatars` prop. This lets you control what your AI assistant looks like in the chat and what your user's username and avatar are. + +There are 2 avatars, `user` and `ai`, and each have a `username` and `avatar` attribute. The `avatar` is a React Node and the `username` is a string. + + + {}} + allowAttachments + avatars={{ + user: { + avatar: , + username: "danny" + }, + ai: { + avatar: , + username: "Amplify assistant" + } + }} +/> + + + +```tsx +, + username: "danny", + }, + ai: { + avatar: , + username: "Amplify assistant" + } + }} +/> +``` + + +## Response components + +Response components are a way to define custom UI components that the LLM can respond with in the conversation. This creates a richer experience than just text responses so the conversation can be more interactive and engaging. To define a response component you need any React component and give it a name, description, and define the props the LLM should know. + + + {}} + responseComponents={{ + WeatherCard: { + description: + 'Used to display the weather of a given city to the user', + component: ({ city }) => { + return ( + + {city} + + ); + }, + props: { + city: { + type: 'string', + required: true, + }, + }, + }, + }} +/> + + + +```tsx + { + return {city}; + }, + props: { + city: { + type: 'string', + required: true, + }, + }, + }, + }} +/> +``` + +Because response components are just plain React components, they can have their own state, fetch data, really do anything you can think of. You can pair response components with [data tools](/[platform]/ai/conversation/tools), so the LLM can query for some data and then use a component to display that data. Or your response component could fetch data itself and the LLM just needs to pass in certain props based on the user's message. + +### Adding a fallback + +Because response components are defined at runtime and conversation histories are stored in a database, there can be times when there is a response component in the message history that the current application does not have. Response components are saved in the message history as a "toolUse" block, similar to how an LLM would respond when it wants to call a tool. The toolUse block contains the name of the component, and the props the LLM wanted to pass to the component. The LLM is never directly sending UI code, but rather an abstract representation of what it wants to render. + +If the AIConversation component receives a response component message for a response component that was not given to it, by default it will just not render anything. However if you want to add a fallback component if no component is found based on the name, you can use the `FallbackResponseComponent` prop. You can think of this like a 404 page for response components. + + + {}} + FallbackResponseComponent={(props) => {JSON.stringify(props, null, 2)}} +/> + + +```tsx + ( + {JSON.stringify(props, null, 2)} + )} +/> +``` + + + diff --git a/yarn.lock b/yarn.lock index 25fb27bfcfc..227e7eea1f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,6 +350,15 @@ fast-xml-parser "^4.4.1" tslib "^2.5.0" +"@aws-amplify/ui-react-ai@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react-ai/-/ui-react-ai-1.0.0.tgz#db6dede6b42685f9c03293374a1aaf195de7f02f" + integrity sha512-Y+ezPjjdUajEjN+naWhaBn7TQ4nVEtB0EylGwI8YrLKHFQVJaEGBWvvw9mfiUl3krWv+utP+EhagTM8Zk9cMBg== + dependencies: + "@aws-amplify/ui" "^6.6.6" + "@aws-amplify/ui-react" "^6.6.0" + "@aws-amplify/ui-react-core" "^3.0.30" + "@aws-amplify/ui-react-core@3.0.22": version "3.0.22" resolved "https://registry.npmjs.org/@aws-amplify/ui-react-core/-/ui-react-core-3.0.22.tgz" @@ -361,6 +370,17 @@ react-hook-form "^7.43.5" xstate "^4.33.6" +"@aws-amplify/ui-react-core@3.0.30", "@aws-amplify/ui-react-core@^3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react-core/-/ui-react-core-3.0.30.tgz#e86346d5293bfa7f22aae9832fc0bef94ea64a83" + integrity sha512-3AaUSC1Mg+yr7TqHfp34QpP6ICjQl9wUR+x7KxsETc7m5tCv4ANGXOD7qaADCi3CEw2IChBFuVu4NFKsGJbjqQ== + dependencies: + "@aws-amplify/ui" "6.6.6" + "@xstate/react" "^3.2.2" + lodash "4.17.21" + react-hook-form "^7.43.5" + xstate "^4.33.6" + "@aws-amplify/ui-react@^6.3.1": version "6.3.1" resolved "https://registry.npmjs.org/@aws-amplify/ui-react/-/ui-react-6.3.1.tgz" @@ -376,6 +396,21 @@ qrcode "1.5.0" tslib "^2.5.2" +"@aws-amplify/ui-react@^6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react/-/ui-react-6.6.0.tgz#1d10f435286fcac5e07330e318a0e30c0c84de94" + integrity sha512-BHth+/CBZ8XE0IBpq+L/7mWbvGRIQxjtOmW50593hW7PvtaSXXDzPHZXRQ5nRhWs1anhxNWDiv4BKuiey6YpOw== + dependencies: + "@aws-amplify/ui" "6.6.6" + "@aws-amplify/ui-react-core" "3.0.30" + "@radix-ui/react-direction" "1.0.0" + "@radix-ui/react-dropdown-menu" "1.0.0" + "@radix-ui/react-slider" "1.0.0" + "@xstate/react" "^3.2.2" + lodash "4.17.21" + qrcode "1.5.0" + tslib "^2.5.2" + "@aws-amplify/ui@6.4.1": version "6.4.1" resolved "https://registry.npmjs.org/@aws-amplify/ui/-/ui-6.4.1.tgz" @@ -385,6 +420,15 @@ lodash "4.17.21" tslib "^2.5.2" +"@aws-amplify/ui@6.6.6", "@aws-amplify/ui@^6.6.6": + version "6.6.6" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui/-/ui-6.6.6.tgz#adf39ea025ed2f35e46bb01323f8d73142b41f59" + integrity sha512-4fBMO5+saXaAgBwhYbQIgudVcK1B9oHuG3WizMpImcYUbB5aL4B6NhFtmSz3DBBqAeqrZHUuUV7gWibU5JxAGQ== + dependencies: + csstype "^3.1.1" + lodash "4.17.21" + tslib "^2.5.2" + "@aws-cdk/asset-awscli-v1@^2.2.177": version "2.2.202" resolved "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz" @@ -4574,7 +4618,7 @@ cross-env@^7.0.3: dependencies: cross-spawn "^7.0.1" -cross-spawn@^6.0.0, "cross-spawn@^6.0.6 || ^7.0.5", cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^6.0.0, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== From d3c48f3d046ddcc0936e837ef3cd87df6532918a Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Wed, 20 Nov 2024 15:02:31 -0800 Subject: [PATCH 02/11] fixing build error --- .../ai-conversation/constants.tsx => components/AI/index.tsx} | 0 src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{pages/[platform]/ai/conversation/ai-conversation/constants.tsx => components/AI/index.tsx} (100%) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx b/src/components/AI/index.tsx similarity index 100% rename from src/pages/[platform]/ai/conversation/ai-conversation/constants.tsx rename to src/components/AI/index.tsx diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index c378c461ef0..9625d60d700 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -2,7 +2,7 @@ import { Card, Text } from '@aws-amplify/ui-react'; import { AIConversation } from '@aws-amplify/ui-react-ai' import { getCustomStaticPath } from "@/utils/getCustomStaticPath"; import { UIWrapper } from '@/components/UIWrapper' -import { UserAvatar,AssistantAvatar, MESSAGES, MESSAGES_RESPONSE_COMPONENTS } from './constants' +import { UserAvatar,AssistantAvatar, MESSAGES, MESSAGES_RESPONSE_COMPONENTS } from '@/components/AI' export const meta = { title: "", From f0a6e600fa035e47d141fa90ebbfb3f98c09c0df Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Wed, 20 Nov 2024 18:23:52 -0800 Subject: [PATCH 03/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: dindjarinjs <187552781+dindjarinjs@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 9625d60d700..3091ac34888 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -64,7 +64,7 @@ The code above won't really do much, but if you wanted to play around with the c Make sure to first follow our [getting started guide for the Amplify AI kit](/[platform]/ai/set-up-ai) to set up your Amplify AI backend. -Conversations required a logged in user, so we recommend using the [``]([platform]/build-a-backend/auth/connect-your-frontend/using-the-authenticator/) component to easily add authentication flows to your app. +Conversations required a logged in user, so we recommend using the [``](/[platform]/build-a-backend/auth/connect-your-frontend/using-the-authenticator/) component to easily add authentication flows to your app. From d70613e961104cb7c8c040856026af09bdb6120e Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 00:31:47 -0800 Subject: [PATCH 04/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 3091ac34888..1747f6f9773 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -108,7 +108,7 @@ export default function App() { ## Formatting Markdown -LLMs typically respond with markdown. The `` component does not have built-in markdown rendering, but does allow for you to pass in your own markdown renderer. This also lets you customize how the markdown is rendered with things like code syntax highlighting. +LLMs can respond with markdown. The `` component does not have built-in markdown rendering, but does allow for you to pass in your own markdown renderer. ```tsx import ReactMarkdown from 'react-markdown'; From e2a9ed5bfa7d00bf0a24187ad51397968d6d805f Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 08:28:33 -0800 Subject: [PATCH 05/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 1747f6f9773..c4adcdb0cce 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -120,7 +120,7 @@ import ReactMarkdown from 'react-markdown'; /> ``` -Using ReactMarkdown with the `messageRenderer` prop also allows you to add any markdown customizations you'd like to use. For example you can add code syntax highlighting +The `messageRenderer` property lets you customize how markdown is rendered within the chat according to your application's needs. The example below demonstrates how to add code syntax highlighting by using `ReactMarkdown` with `rehypeHighlight`. ```tsx import ReactMarkdown from 'react-markdown'; From 1a3cf4cca0748a7f4e6e8f80bc0abd3c4454da57 Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 08:29:40 -0800 Subject: [PATCH 06/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- .../[platform]/ai/conversation/ai-conversation/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index c4adcdb0cce..6749beb99c6 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -282,11 +282,11 @@ There are 2 avatars, `user` and `ai`, and each have a `username` and `avatar` at , + avatar: , username: "danny", }, ai: { - avatar: , + avatar: , username: "Amplify assistant" } }} From 598f02c975f9e329908c054b97955556d9c5550f Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 08:29:51 -0800 Subject: [PATCH 07/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 6749beb99c6..0adc2938d31 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -200,7 +200,7 @@ The welcome message will disappear once a message has been sent. All messages have a timestamp associated with them that are displayed next to the username. To customize how the timestamp displays you can pass a custom text formatter function called `getMessageTimestampText` into the `displayText` property on the `` component. This function will receive a `Date` object as its argument and should return a string. -The web has a really nice built-in date/time formatter you can use called [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). +Browsers have a really nice built-in date/time formatter you can use called [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). Date: Thu, 21 Nov 2024 08:30:04 -0800 Subject: [PATCH 08/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 0adc2938d31..9dee3213f60 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -140,7 +140,7 @@ import rehypeHighlight from 'rehype-highlight'; ## Rendering images -The `` component will render images in the conversation history by default. You can also customize how images are rendered, similar to the text messageRenderer. +The `` component renders images in the conversation history by default. You can also customize how images are rendered with `messageRenderer`, similar to the text example above. ```tsx // Note: the image in a message comes in as a byte array From 8ae737e279f1d5409063f1a34b0e4a77e09d7b4c Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 08:30:18 -0800 Subject: [PATCH 09/11] Update src/pages/[platform]/ai/conversation/ai-conversation/index.mdx Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 9dee3213f60..2411222a3d4 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -126,7 +126,6 @@ The `messageRenderer` property lets you customize how markdown is rendered withi import ReactMarkdown from 'react-markdown'; import rehypeHighlight from 'rehype-highlight'; - ( From a8b7631c6b6c8f45af42d3285ee9cfb330c0ce5a Mon Sep 17 00:00:00 2001 From: Danny Banks Date: Thu, 21 Nov 2024 11:15:41 -0800 Subject: [PATCH 10/11] updats --- src/pages/[platform]/ai/conversation/ai-conversation/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx index 2411222a3d4..cc5b19687ea 100644 --- a/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx +++ b/src/pages/[platform]/ai/conversation/ai-conversation/index.mdx @@ -343,7 +343,7 @@ Response components are a way to define custom UI components that the LLM can re /> ``` -Because response components are just plain React components, they can have their own state, fetch data, really do anything you can think of. You can pair response components with [data tools](/[platform]/ai/conversation/tools), so the LLM can query for some data and then use a component to display that data. Or your response component could fetch data itself and the LLM just needs to pass in certain props based on the user's message. +Response components are just plain React components; they can have their own interactive state, fetch data, update shared state, or really anything you can think of. You can pair response components with [data tools](/[platform]/ai/conversation/tools), so the LLM can query for some data and then use a component to display that data. Or your response component could fetch data itself. ### Adding a fallback From 68b5f4bb20c5cb133d80aa3a0d0b65d80b4f5d32 Mon Sep 17 00:00:00 2001 From: dindjarinjs <187552781+dindjarinjs@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:58:04 -0700 Subject: [PATCH 11/11] chore: bump ui-react lib --- package.json | 2 +- yarn.lock | 54 ++++++++++++++++++++++++++-------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 67ab2821de9..92083486721 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "private": true, "dependencies": { "@aws-amplify/amplify-cli-core": "^4.3.9", - "@aws-amplify/ui-react": "^6.3.1", + "@aws-amplify/ui-react": "^6.7.0", "@aws-amplify/ui-react-ai": "^1.0.0", "@docsearch/react": "3", "ajv": "^8.16.0", diff --git a/yarn.lock b/yarn.lock index 227e7eea1f1..a97e5c13c88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -359,17 +359,6 @@ "@aws-amplify/ui-react" "^6.6.0" "@aws-amplify/ui-react-core" "^3.0.30" -"@aws-amplify/ui-react-core@3.0.22": - version "3.0.22" - resolved "https://registry.npmjs.org/@aws-amplify/ui-react-core/-/ui-react-core-3.0.22.tgz" - integrity sha512-uL5jspqvTZhpqH1inPV3ifvrzIVgIIriXPgjz4BaceDm+1X03Hc3tfq5TiUKW8PdpuWF6riXXBP3MEFsk29OGw== - dependencies: - "@aws-amplify/ui" "6.4.1" - "@xstate/react" "^3.2.2" - lodash "4.17.21" - react-hook-form "^7.43.5" - xstate "^4.33.6" - "@aws-amplify/ui-react-core@3.0.30", "@aws-amplify/ui-react-core@^3.0.30": version "3.0.30" resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react-core/-/ui-react-core-3.0.30.tgz#e86346d5293bfa7f22aae9832fc0bef94ea64a83" @@ -381,20 +370,16 @@ react-hook-form "^7.43.5" xstate "^4.33.6" -"@aws-amplify/ui-react@^6.3.1": - version "6.3.1" - resolved "https://registry.npmjs.org/@aws-amplify/ui-react/-/ui-react-6.3.1.tgz" - integrity sha512-n/wTjMYYuAhpgpuQ4o+9+IgvNsdjGqbfaA4bAXE7b1apIOfM8HTaO2zant85x+geDl7rbaa1y/z62T4H9AksMQ== +"@aws-amplify/ui-react-core@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react-core/-/ui-react-core-3.1.0.tgz#236ecf10194a27dc57fc983c679ab1de19b8a00d" + integrity sha512-Nime3qjJRQyfRDDA4bnAaFVzRfEBddZFP8NhVIb13z7Uw0XoPQzX+dXwuRW+Bjt2FJnlIUHh7Cfkt0m4PedRHQ== dependencies: - "@aws-amplify/ui" "6.4.1" - "@aws-amplify/ui-react-core" "3.0.22" - "@radix-ui/react-direction" "1.0.0" - "@radix-ui/react-dropdown-menu" "1.0.0" - "@radix-ui/react-slider" "1.0.0" + "@aws-amplify/ui" "6.7.0" "@xstate/react" "^3.2.2" lodash "4.17.21" - qrcode "1.5.0" - tslib "^2.5.2" + react-hook-form "^7.43.5" + xstate "^4.33.6" "@aws-amplify/ui-react@^6.6.0": version "6.6.0" @@ -411,13 +396,19 @@ qrcode "1.5.0" tslib "^2.5.2" -"@aws-amplify/ui@6.4.1": - version "6.4.1" - resolved "https://registry.npmjs.org/@aws-amplify/ui/-/ui-6.4.1.tgz" - integrity sha512-0rGGJjnd60gZNhjqDepk3VpCpzyJDE2+xevVg0iqM8APKpyQ9XRWisNLIvglQy7p/3CauXdw8U0NZVcu29Yhrw== +"@aws-amplify/ui-react@^6.7.0": + version "6.7.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui-react/-/ui-react-6.7.0.tgz#a3fc28980feee01f319448c360307a5ccc65c7f3" + integrity sha512-3H97gz43+iaVNPqkQIiFj4Ko7zJLyMGtZScfNyt6PK4Ntuit5ZP6hc+Z7BtNsNEkfnAiGL1BiNOuD1IBfsnifw== dependencies: - csstype "^3.1.1" + "@aws-amplify/ui" "6.7.0" + "@aws-amplify/ui-react-core" "3.1.0" + "@radix-ui/react-direction" "1.0.0" + "@radix-ui/react-dropdown-menu" "1.0.0" + "@radix-ui/react-slider" "1.0.0" + "@xstate/react" "^3.2.2" lodash "4.17.21" + qrcode "1.5.0" tslib "^2.5.2" "@aws-amplify/ui@6.6.6", "@aws-amplify/ui@^6.6.6": @@ -429,6 +420,15 @@ lodash "4.17.21" tslib "^2.5.2" +"@aws-amplify/ui@6.7.0": + version "6.7.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/ui/-/ui-6.7.0.tgz#f31da1515a25c2fac3d9e17c9638e619a988baf8" + integrity sha512-6hByYfFBQRjsFMoVGdCWMSdo7rwMgz6rxxdWV0xuHb4j3tsPEI9ZhRXG0Z1ivtQFAM3Uaz0D3hcg1kp6QFdCFg== + dependencies: + csstype "^3.1.1" + lodash "4.17.21" + tslib "^2.5.2" + "@aws-cdk/asset-awscli-v1@^2.2.177": version "2.2.202" resolved "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz"