From 3b0e3ee7dbe83cef1a1f2e866c7e0c242afe6217 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 3 Apr 2024 19:24:56 +0800 Subject: [PATCH 01/37] Add Package vsix add getCommentperfixes --- .../node/pipet-code-agent/.vscode/launch.json | 57 ++++++++------- .../node/pipet-code-agent/.vscode/tasks.json | 34 +++++---- .../node/pipet-code-agent/PipetCodeAgent.vsix | Bin 0 -> 93456 bytes .../gemini/node/pipet-code-agent/package.json | 22 ++++-- .../node/pipet-code-agent/src/comments.ts | 67 ++++++++++++++++-- .../src/getCommentprefixes.ts | 26 +++++++ .../node/pipet-code-agent/src/review.ts | 8 ++- 7 files changed, 163 insertions(+), 51 deletions(-) create mode 100644 examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix create mode 100644 examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts diff --git a/examples/gemini/node/pipet-code-agent/.vscode/launch.json b/examples/gemini/node/pipet-code-agent/.vscode/launch.json index 670d6e66c..21644ef98 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/launch.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/launch.json @@ -5,30 +5,35 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Run Extension", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ], - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" - ], - "outFiles": [ - "${workspaceFolder}/out/test/**/*.js" - ], - "preLaunchTask": "${defaultBuildTask}" - } - ] + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/out/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": ["${workspaceFolder}/out/test/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Package Extension", + "type": "node", + "console": "internalConsole", + "request": "launch", + "args": ["package", "--out", "PipetCodeAgent.vsix"], + "runtimeExecutable": "C:\\Program Files\\nodejs\\vsce.cmd", + "presentation": { + "reveal": "always" + } + } + ] } diff --git a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json index 3b17e53b6..067c0ed1c 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json @@ -3,18 +3,24 @@ { "version": "2.0.0", "tasks": [ - { - "type": "npm", - "script": "watch", - "problemMatcher": "$tsc-watch", - "isBackground": true, - "presentation": { - "reveal": "never" - }, - "group": { - "kind": "build", - "isDefault": true - } - } - ] + // { + // "type": "npm", + // "script": "watch", + // "problemMatcher": "$tsc-watch", + // "isBackground": true, + // "presentation": { + // "reveal": "never" + // }, + // "group": { + // "kind": "build", + // "isDefault": true + // } + // }, + { + "type": "npm", + "script": "build", + "problemMatcher": "$tsc", + "group": "build" + } + ] } diff --git a/examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix b/examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix new file mode 100644 index 0000000000000000000000000000000000000000..3b586618b49a78ef04bcece2b163d3e4cc7ecf7f GIT binary patch literal 93456 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

ks| zrAhud`SosNg5F`W+&=vUOiv!(Vj(nG0v|oL8pAaovyD5=>AY7z7~dG*-xi-1kw1a2 zJ{48gOU5`W1pY=1T!?Xu$scxheS(59Rz|1>L4n}CDTo*w(d5n~`ul`lY{^56FyXYJXU8Xb;ObtFojC^Ys+|_6}}LD0G-=96vLl zBxFId2azb`Jnlcoswhl0g}w!Hc}iV}U9SO?JfC+G`0*(%iV{W?zNae03mM33 zGl(Sijrt>IioGff8AOj>NmA?;P!vLURro`d&GFOD2*CBK{V=#udZ{)^C7kQfBs}p~ zof`QRmIMtIl4l;b`LW>;@XZnkNdC2VvJHv;LCE6~|JX&sM=fcZ*pH-}(5VXuHL3ex z@aXAJvgLw)gJH`K6&csNQOKNYew_=-{b7i4k`J7{k0@MeSMR!6-L!63?VZ`~(Q@Z9 zW>qPBKu24nmRQ#BtKWN+?bYdl)Y90CE=pm>7NlxfIO-Q zE&$0#WEuECH5$P5YBPtXQi0NiR_{?p*w*FeHRYpt3(pYixnbSyIf*cHn}|h%vW{A0 z)jub$etb%-L^2r-Trs1G(`*y_slimX24ZGN{aw?_7xvludCO~(0hWh$u)>Y4R@+oz z2IP|;s(XqPWLLEdt2)e4dm>MYw?P|uQ(9p@Q3m^9o3`o+Y<@Nt!E*GLalI;JR zxug#`O-q~ zfZ#j?=SyE^?x%Wu5e$CU#DsY0-wyX5oME3c9WP{vl$J!s-dXsnM-|-zQD_(lPN)lhOZ77fmSW|JoR(SgKwhzHEN_Df*-Fl z2g~YN3qR$NxZ-`=zrwx%@?~q~h~p+6{|b#g-ga)Us2Z~CGkcuY0VgV$QQCGVB?>J%~_JWNTa9 z;brpMw{)%w%y_7emyT;FtRa-p&YKg!itx4#cbV*MD59~D^52KpOM$(=fVEIYU^n1%nkZ`# zV?4{?H@3%$?VXSfDEMR0hCd3&@#GLtA5p!WWi0~g7nX|BffEX7xi>IUyRHXq{!GqO zzs_2e{+DyPKkM5+WO*I=rrx`w{M%%*T+3D^2QJcYfFVvG z3!$r++&scdOS=;3EukVlM)}qlJ%^&D|1D*@Nsdyazei4sHUqpt`bQE5!PCw(! z@NYP-lV~g`IDV>a{o?@Au_#i{PEAc>A8!&OPHUAfPK{BV;*HSB@qFD#ORE*_Oi-}G z`9b}xGAsWM&S0M1sK0*7Nw4hnWd~DiX~wL4hm17eCg;Ep9L4!HNWVz=$?M_GSCv^{vd}Qi;8abU4-H|yR7-9!>7&fs}xOB+z#gradf_n z;gVJHeZvtUP^MdtR+gM&ZiuXji2{UWuNJRDsJaIc2)}bMdQ7JEFoIttI|XDpfZ@PS3tS3dQLs#F0kJ8S57q(IX?vA#6)%yE@@t>ykw*F8f6k zwJ+^3_8;rwBxTd|AM0Xl$I~F}K(_RMSeG*)W)NJ?ascV&8@agpXD*|i&Q02x7DA|4 z0$3(w^uNir}-dE5H8 zaO-DSv=NQcX3(sU(VG&8rzbDt3Um1C{XjzEpkP+)*i&tLGIjf1K6%s=i-if!#r%~a zL(-G+57lZZyb&hz4C}!bSAWIjtsVNw^OS*VDTQOez7Ko)=MdGGpiDexxT+}Foc~t& zNP{MuGVfl;_+l1!fi)ahIVTqE>e|-%%|4esQ?^Lr4a!M~V#B8`hW-uF@DUp!HZ5Gl z8#7(Mxj>q@A%}#N$zo5DwmfN=uFSr^PLw?+tzZR$0yol!_+JXwNxN11unOwgJ@FCs zlOoc15cgmVpAagf4*A{M`nU#9p#H*Vy6gU}Tw-jBnDVpWer~;53qLE1N{rbej3P!K z$*3i$3@<}BV?<-m;?GxQ5^01;cxdkl+^74CggYWYXST!>yD2=1RfNpSTto2Xr8+zz zV#J5Pl=QiUlDb1>)o1b5M6lWKg9Rl}>5Sq=L@^l3?d|(}v#u_>#x!?6k(AZN?7$%VJ_DFZxm4pLJE>**yFPUl*D2s&-c`BI(W zG)aNUr1MdO*ohyxSg^EPYBAitB49-uEBEtY$Ap<55ngtxiro3wA;XD&XQlIGkg9^l zoO>&{=lYD`9CAa{-a$Qi0=eFO8P6f7DoT4cCr7D zcA*x|(_qfmN3Q`lO}ibc)C!JbJGubR9j%%6Ut~Fys1T(dll3~oE@2W->pOPSoOiCF zNqyuB)}Kw1X;)D|=xgP%Xo>Kuf+(Uy!7)gI&^31VzK#IzC1zqi3_D&+=+6om;W#7} zUaCE)kYTH+@9S+wknJW3rj((k#63^CU^=kVF&BhM%%@VqTdql8LS$3Q+R`5(G17~# zL!>=X^Ccx!{$pcxS}$3#qy_xh&D@ANl=hNjKwE(|??YaWf1_n|Z^NzEAA6TtU;h?f z76;aPxI79xdOa3bA|6JrP+hZTJQjOnGq@DS<(USw?=k6srQgstrY!ePAv@xpW~5`R zW|geO+Jf>>#NKeXF&X4DD^$%ap5wE2H0}*pL8Yf>(Jk#)5=p0RlIJ-%lX_PWdt?4e ztx+|RdngT8$~|Z-Tnuy+1_zKf|3`Ansi9dxxy&RfV=An*WlefgoT@^#yZyruoIH8bv!)*I(aKqEZ(gz4g^q;p`$CEKxBjI`Pt#-W7}MoK`6gS6_kMN@L`>9r zS4Uwz?YeXwHIOq5+cSIIp z`hK!KJx%6Ab!%5Wf(7loX{#eIybZ69W}}zMp*p}Iw~8={T)|c;(NE(>ubAZE(6BGq zg}Xj`cH>%(%WI{d)~r}Vth-5)%hGOB^55L}hfyn{D;7uk02-}v!d78ME+|$IBT7i` zcadQ;bTzCLE!l}C)5VJqNTuToD%Gp{mWIXB@rxaw71-$A6$OjxHH5%2Wwo}F|?fJeTf zz82bN3wl&(i7tmU%X$L`@g;+toeiVVh;+@qqf(uv4(mj$iv25A@DO6`Q;89qDtjo6 zj?Za0rz_U8Wr^G16UIA4$|E;6n>9BHSFZBTe~Ol|6k>9O;x0_!_9n9%=kx>n>$RJR zTA4BOs~~U@@?N>8=t1Hzw4XZgOp}?j2NmaxN2Y+kd>vIKmO-uXoYA)q(%@ zE+9q26<#%4wEx??m_uD3{d$**sSbUBSI}nahh};sZ<>L5#}%4ovh^@;HBowN#7{ZR zbCWX09`8J(x`dz5X~2H!k~vFp6<}KL`f(;TP;GTviGAb&R_$RXH4(p&949deR4O14 zpRAF&M5(V{Syq%&kMq_BIC_0`iHJ8ysGz%GEE4*u~10~Y#@;1xB^1fz`{?|Ml98>q+S zsD>cyX(G+`pjx-prFXk`7;{(~in!$ZTTR*j-`3Pt&%i>@#OS~3Sd^$GyFrH(eDgsW%@rmAy|LxY zD6EfxVXBXdRJB=!XrCo`PSecjg9N|dBkMd*66D7sJubZe@anZ=z}bxn08hvi4>@jzwe`HBD)Oz^rGpT8K?OCs~p{tHg7BX6p!bdNd5Ou=~^OP#Te1hoEQfmaXG_Z@i6?xj%6v-*I z>zinUa_E#b8#M24phKCI05- z!7JHO?bhLb^>nTB=P3IFbM)cxMJT(meGJD-+l0cesYa$gDa4L*BF0Za3H>6Lg@8(L z5=~aNNPv@T8ywcT!L;X=gbhEyX*VB0!h%v5Cb4>sx^iS{a9-kPu$VY$e!O%J*fBTg zLv7sx=pVoVfD_Q7Kn7WvZHUV0tk~g~opf3!Y2*X#ySp9j7f}<1by*;|X0n{`l za-3`-T}v6@Map$QKdZyc=i)4KH-Va z#BjKa)RC053Pa<{?C>8(yu(Kdpk=i}V=SB=39q1*R&O$_UsdwZlugYA^gMLNk>hO$ z*eUeF?6Gs9cbRmSPQCMZCr%q|2Yiqap(bWXo7PDK(O$H{BQpePac4KQu`BGUKjlzpxe$#2?K54=0-`SAkwhZ6=+rwCPj0=W5Kc7{kD(%2 zt}0hLS^FxoS0Q?|afl;>k5?IM?)GbUq|rf*cXdRlgWxp`SLQ=H>D*+{kLt7GU9NS5 zmJ8v2ls2PgaR?m|Ve&ZR6=o&zphmi69sSJ`CM`_s14pyY`7^_c9Ih+ zIQ$&WYVsgkS*y-3WGiv@<;s6~kcyo}i6_jy(m>7V$_lBP3pa%MaD92L6Xcz8e0U7p zuI1y$>$|>|(SUp+9otD9U3iJ2$%Rc``zipDW{lt`7gj8&QjIO&4m($?;#>F{+a4xTW(lL3dz3_?=5$dRnhD|)@&-U&Z;_LSz0C;N}wGlaC={|7}9YIKMgw zl3g1yQ?^D7C62DY>jj*esaW^f+*jv&tAvg*ci^K|%%7q&l3WdK=z#&70}i&}mfPRi zE`)7bBt~kUGQ<-C%Q7trae9Wg$_W(;P~SrOIRyQv1t!qNq1U}(AfiuL73osjk-}x& zgcVkFv{p+Y?AK^bF}6-oE%dcdOKUBuR%l?YIM)+v=HBw#Vy3C-Zki_P8hU(xJpo=G z!zZpR<6nAP-tQP&go6T3ihI1>MhQY)AS@e}6pe}9f?7Ug$sZeO4K`8d8M3}Kk+)2~ z5W(Hz972%mQ3>vHHXU1PN)vzABcrQen6{1X+J`VB%1p&7^U?vHpX-EDLN@2p=FAJ4 zCP}{QDgTXIsv9R2*K>v%uiC@J7SYlTOq0PMOm(dk-R?988eT;KNY0tsGUA0($?4qi zeZv{`i5w)y#^7rAqnJ*LqV+6mTatPEA)_7aBk1|exO`SHj6XZ{HPhD9eDw1~dGDX; zYu2y&7%QyEgN19YNm%qhJh;So#vB$@P6hB~H|2cK$E?jwlH0ovSvo=bi)6EnW9Tz2i%hUhYfuh5Y(u_~k2igU5fM`j;YJX;fS%m*IBhdVxX9P3HAEI3kMo}qY1xax^Madtc-I^aAqK#3H?PpayH%2>n zxu``$S%}7))k|zhf{oKwNGKdki%7ntasBGO8X6f528RT3jx{pi#+fNovJJj*zo1P? zCf4H` zdPU88(iFkX7DWLs4u}{sqLp{qx>{*`qW$EVH+y?$Fjs=ou^@l?FAg|e%vsIB&n?Fb z^`2EzG387H@D&HRVg{Hd|1^~Q>S`rd;!X0YLQf3iYd5M9XN>%2>jmtz2q6dCO+GPP zTb?F014LL>Y)q9XZvu7qCzH`8Ym+P!sFL|WDOjacdg!ZVpa{Ta30F}D870P$1ZroA z?375cnu~+LQ6YjR)U0DG&~=R<7D4bdZzOP{>3kr3flB}`&;|%fV0^zrHRd6xSVz}D zgQ?k3lo?c&EucbCHo!6W+OXgO29?=#t#m0oPLm+%uV$ z2l8azt1PR#a3Mpn{SakvNBHa57e}@Qa285dy%re8%Ht^?@JTZjNwB@XhbNT9S7z7g zv%t5`xk|v!ix4KhrJNclt%p$x?+IHNP}mIZ2MT1Cn{YYI$rAIVsoh70(ER~75&>5Q z&ywNu#lfeBs4}(@ZDvtC6KE}aA`iMO!Ybv3Xp<%&0&FfR)FN>nB`l32H_lg=m&|xi z5e}Iq#(;%O-9~0(LN&mHdw@H?SGHL0Hqn{7ug11yqAUh?1&^$ruLVmN)NZX&QWiQC zMLM}I$>w&q*W(2IrlGvW!bpGoJh*K)0B)cDwDY~mA&Z8MBdkH-Y#`TNi^1A z`AS#&%e~;&%J;`{-v#P$9ftXl@8Y-U9t#dIttG};GZ?Oe=3k-m6zDI{3}6{Zipjsq zm3O<4h!ndRaHS;@4UcsW>J`)^W+l%8Avv@#XnMWDUhzQbo!4z#GAHe*atHErf31^Uj7|=1d>$Oap}~_ zD%!JFDO#j_+8LDTfjYF8!QCnYfxC!MZCX$c_-Q;`gJv9?;AJNF28>S9skomgSSC z*4^PHY2!LtZ~5v$I(7v{>qJHdtCyVZHpSmR5PRI|Vc=CWO!itNGWLZ)kpR?3Iqmb4 z{p+oktK31|g}^$vh@K~M235a4Y;jPF`|BzKmW*+PZgi%>@_|t3PaS%=xXdj-C(GWP z>rWjU8at0j0ahVoawu1OhiQjRP}L*P-62C{`PP5@$j#8D}9mp><}Ln$_SHL)*b< zXj#gS&w_s^!xa>RwN_@txHPat2fGg0=5I>?>+ksSHl@vAp@r^8IF5E$3w1;daW^S& zLFiVRDB7NjvfyW&JLM&#%mGeFepUfei3{%Sl1$9y?a~Bj=hg1&w-cAj{kYPzkAJeD>-<~n+S_S(hysgP0 z*JH@RCGV&fmXTdJL|irk4z7oKRJ=>v#g5f}Fyj$vO~}Y+u1E#vyVyOBg-WY8>c%bG z0uHvB;+}r1nr#1oU=a!8&e9q4FvCH*=GuEC-|ZRf^e@Qg1_o_A z8Ev}2cros!?)n56vqn0;3IyGz+lTeU&Q~3%T$l5XN4Bl?(bx+FOI?OePpY2bWc^e0 zJmcH)1}|T$Thy-HYn>1Lsf-op1G5tr6Pzw&RUpC)c}@4Qa&J! zBRS4GS~NY6Rt>aTt`Gs&L)q?#K}|d$(;mP~U(4l<4DuX$7B;3Hr<;m~ISnqUfViwU zfTISfQ!A{0T$YvhI?tO%=M?MbCx0E! z=LLP&?*I`xzNwA|w&mod^m>pv@})7tX>N zxT=Mxh?f2<5+H5}`YXJ^g=s+h$>%|mut+)Nf6+I2J^Ueoy*R>z`EK;udu&?*!R6ga z;iqJ{ErZ9?u zGJAFQ;jMT- zACtiADkI4wuFB|nI~?)&j5}p%XjYRgJ%-c>T(~F zL}g%3&0%q;=t1BwKg{t^-KZNLN5s*oIf3{6<9`;yTFg2XKVN|s>O#IN4!YlvrnFp8 z&CT|n=}yHlW>ao12UcY*zL&9VHrUo8y)0M+)F&AD!`m}erixv6>P`2!=NK!Unb$2D zx6eL`*L8c+;b9E9F+M%}J#vi_{F)Y=_dlv|yKG8&K6F+il#!KlDFYe+BQG)&w-e9s z#T--U7Pl%MM(wy^rgg1>_n}}9SadZJ8Y(GV2qsHCpb3`P;r2%v_m0&?0fJzM@6*lB z-u*lL5U2e1j;w6Dv&a)*MtY9y!V!Etc7k|4)aNhw^i=f(FCZ$@DhiD z{#QR{{jC4F{yb&<&;I`^Bm94h#hL!we%M)?Fq+y~(>pr3{b&lg{&a{xuHWV-@FjNr z7R7x)l(;*){R#aEvJd9o;sF-BTwQ_`@p(ODvpK=Q%X^QN#AZoVH1vu>=IX5{J&!a} zbtr~nf|EXWyn*u46%);;#SY<7V;^_r#Bsz>@ZmH#Bh5Br#hfC>EfXxa@pkvep}O8+ zWDQ^rbg0K>jEZxp6mc0a>RpIhev*ftzkXvtjhh?A@z^+C%d-dc{&h|K7uDUd;r+y? znYZx$qRPC+wNAt=@bUM`*`$sC{|p2uYq9`${`4B-KSs3v@9gs9{`ueR5<1u5Fu;Ta zvAd<{Z0F{jO9YAps*UoSik4*>Ic6ZooEHP-tV^>(hBEWw`8&^FKP@Qbk8$U!#x{tR z>Ff~DMWH!NLN!FO8UF%4waEPTnQOQ(Rw>f=m5Y}@v@s)-XD)mgQDPF^Sx9AEnYw<> z#FVspRVP&y{G>~M@43J(_3jc;A{=L$&Zu#xh@45+knuZ#3y*i;YJ*c)3r`~as$Ti0`g1I2%(E~8U`+?rM`d&;8g%tn1gqtOO$Er^H@N~5_lkjfbI*a9U}Z)ttvB2QlDTFCODj#- z!lEx{M4s8Zu1iJ4yx@Y>+b;X=39NL{23)t#{u>Toj&=AC2OlaA9?^ycKP$u`2hN*t zlM&Sk(QD(IFt|7c;-am5TZ9jR zP3TVlww}*={AP?MiK3-o)dBF}U1Tyf79unSiah3|SZf8+3b+ZjP)0AbzkZ;9dA#nh zd7S!e*no8Cxr`nlI(|zF}Gu--WOnh=ZZV9q3pA zn*}kM`wAB)nUJ6K5s%Q3Bfrs`8@07$u=nGh$ub|)N5mY#2X6dIB6;ckg2(qmB;GbY zu;ms8+Hns&vbK?M>Pl?pWtcrI;Y+shY(hafn)|*kJi@cydJy{~;HqkrSLG>VI^3#ZZ06 zawZp6eMA$`UzHlL$RWo?kEr6)+!NN>vwsqaII{IRUy}uIT8E4N62cAL_%@@$J-5cL zv>9?OKU}ehnC5%g*4h>&jj44MsWdDAJ5JCt>`J4DiLTSsy&_U|6da_vsME9)j-1tI zKs&dLQshCdwBw(Pm#Hts`gwf`}6S`r&(5c!E$VcP0xZ2PxsJ|8!HzPYH zF^GAi+GHl>6e%U~I*pReXFbzRtHO~=l#NZ&y*_u|wB#gfjLRG@UBmg5yT`!Bk8@kL zohjsxaPNP;_UGI<6F?}5Nek{&^yMoOrk2olJ=6s(2la_;_EoU z4`}yZd9P_gw~gMY(Rjy6*Ysihdb+c0LSqrIwU*aSMeUMuM43JTskku99-89$7WDr( zzIWW)vEh0YTFRvz{6t9iM~b48FTQ4XkHKz4JC)n%E<~KFDJf!SgO%Dn-pfLRF6(#ED*WV3$LFY(RH_R ziAd4QI#qe6D9x1VrG2cY$ZvMwce@O$GY?lzno5-IPL_AoIgwc^*&(A7>oj^VC*g4F zNkB1e&{sCj`^#|A-^aTgj{XbzT_;LkSG_PLfO=bfPeZ{f3$5lt#aTt&Qy1T+^bGD! zHZI?QQ-uRGAV?$uTLV0#1gq5UC?1lc*?+sYZPb_@=OKP4JI3gJ+ofjMmc$XdY=`E4 z|7-0w-E5d^tWSGxaQl3s%`iph0RBayxU)5;2`Y3JTFadO;|A1jy8#;fJM4G}eZzKD zu1{l(zphr5o_w5&k)2&KC1|%C*pZDQNDL@wqN!K@CYutT)6%uJQ8#sKqKULGX~T=A zJ5}*P1gl~7X=r-Eo)P-`L1|X}9fG_A5iFz9pyE zsKjWk$|$S@u^LymaZiW0)0yg`M1Z-E6sJZc09?s6H=DtlSI&@NNr}&mgniXGG`4nW z%)hJ@|FSL4R!ZO1KEBTO^VJtlK{zp3i-+-Ux1NYx{Jh-V zeC;QBc$nMezxgAD;}jWcx3l#K(yxACc#@Zc*LRI?n36$ej-06FsxX!5*{)Y%Wzd+- zYXws(9c2uzW(V93+4fo9uC+uf5?X#Xs?g?`1BtOp4TRx>M%UBd7$e@+K*-jw>Ww8; z?Q0BPQEBlF|G3%tu#E)WtTh}Pt>99nEJ&^+W%|{sM z(xk?}I;c16ruD46*Yls09V%35Ha$?qMgwdJu6ce|zCk!OLUo<-qj!jw+ze{ z^cPJ+dz`HDhFHAQLtmH;vQt=Lm|2SLg16G|VR4jL1_6*TCi@@tkqtdMeZwx?r4*n3 z7F+RlIUb`cAZsBmlk==MB@udr51N;7u{WU|Zc;c#4)*olWN@#jbO^P$Wk6Q>OO}3q z32720fi17`Y+2$32_adl zNgg||K#d^#KpIDsq-)xREV?Pp2F|lrj|lmKZI~ z=kz|Twm+@NW#0zem3!eci^kFOLC`(Gr}p|Mm*$cVhA9k`(_7-2>!~DKo{8g|BB=mK z9uLGKz5Ue~H#)LOQzIqhm=pimrVKSPodE!ez3S2}@l_(8tMW#@jK;>2)tZdNnWzdR zk$OUvrA&FwT1;_4&;P*Mke;?R0xN`cm(J4!3#N>(cQ4vh!TamBFP`JQ!|l1`6QO=f z31Zj=>Q%h8;Ivy-KmxIa$jv-})j=<{+*1#;}g1I0LdHl6AeuwZrrn1|y2CFKHH`48iLq0!wIl@e}w)Y;f^5q9DC_ z6Dkc4ZF5QO;X(X{kkJ*^*L(Yh4{c=o&Wd3+=?h*)iv+BFHw9NP113=PMaq?Rz{AW0^U5?(b#2O9`kBs&J5)X zzZ;ZG74)#=jCviTa57%z%VOPo*KgVc>bZU5U$Pz96lZ>AwQN@J@Yn~La|n9Wsb(qC zW4mSh+P2#AI7KP`^Q|J%i}oV2L8f~t6Gl6?FI z2U6Je2Rft6I7aY0@WXtzm9-r_Q8bP8+O4v!vnfbxd%_9)-8C#rSHuDYy*CQ6i$(4; z@x;xC`BUCB^0gx3%VoM@0HcutvBZT)jKb&y_^*Ke3k=S_c4zY69z}+G7le*;;n0I@ zd#zkA(6a4QlX`@U+^MC zrXPk--4eKirylOAlNR7AQ!~aA5tf|$JYt;hfJR@}Y(@&qZ9v`cdUjl_mIlZQXuDbK zmg6Xz>iBy00<|sW`_|TSb=5?U1p82x7LEJPHW}RRWC*+M$Y$aV(i79NF)S+hZFhLp z(2zx)*0eFS-B!D|s15rdcgH!J=rB*aiePnd9GWj}h?f31H|>e#adu9-cxz2i{qNsO^VaH5nI4ha z_C{|l4R$`Vt2FB5a{~$6uF^-KIOdHt+e$$)I)5NNb?jF&5v_Z!;4Q5;b>=&->8hT9 zTX<%3o2kql%f~&`vwmtQQUpp%$Ar@ica@TBq$vy5QsiJ_mh{%UOhv4D0 zvi8!E5+w|f91lPhYsH`+?ia!Y1#fuVTgr}72ZvkH^mp>XoC!<0s88nr1?<7PYcg6| zTi>M~lz{*FAB|NST2}w_Z;0?86YBo^^ojpH6*N>$UUh>BspnYJ%6E6R-vy3Z$->GS zmcIql`+|)%s#zgrG>K-c6g%vDTB^|w9HwW?ac4vSdSwu+{OQ0Qr>VZ@s$ zjrQm5hgDT8xnB;)7J)+d@;#E#>sf>d3lhZVyoqU=!h*w@hki~%hKe5% zU5%nyyKyfbdOkP;@{R@gg9x)EF~((!_mfe62{Yh#8}(7_oOG9M{BI41S;?!J_dx({YvTMDzP z4;Vfvs2HzNxZ7gZX$(K2r;e#AqsjVjnk(PP~L=;kme6GwG zM-*OLUTnDR&@~cS70XeF(yWwyXpl|M)i$4reZB(#ljC#a-?OYuN^UiG)j~6$BsmM` za9pA^F^;aPs>&+=$8cA{L{_AV?D8Iom`b4qFC?(%dr08Fe!6#*fBk?}L-u=yo2gv7 zHZB#+nCT^rtD9=4S{m0^9ZI9cFe|M1ysBZ%WN_=3ybr8Ux!a7^-BNw&Ad|Eaka-GcYg=z#88%^=m&HYnvTwq*Dy@OP=JN z%7jJIHSvd-vPl&C(qm{dSlvS>`{w33I@&urZf$ie8Qr09-{U2$fmKJQ%i=T}XP>bo z%4En|gk}{NO%>C!GWR?DDbpd_TB|X*Uhe7A{jBlPFTC@mxjy5p8`P7>aUxDO>0DXe zUb6|5itZUk95`@ztTp`lIsw)v3rmv;{1Y&0P68xs(IHIRf>J3&HEX64i6$`s(^#UJdadRiVs(}yNx6vsodW_0vsn|xCELQ51d}L()u}Kbe zs+H^xB18e^M=*Ld`-*K~zsPyYkWzZz%Jli~Pe~arfwl&z20!QCrk0_*UcauDpKU|_ zQOtALJkvv>A|ik%Z_Mn&qMPQ;pZv{q`yV=>Yl4VjLM$*a<<&HlnMYo{5FQ^T0v-lb zBR(3|7>O*$yo6ZNv<0Q&Lz}Gg@<4w~hqk#s;A4+jPlN$&-dYw(EJ>Q&-Z-15Svw9J z8?w&(4Pt6_WgMfl8_h|mDqj48!q-^VxVW^=)Sd;H)mk>Oarqz5L`&fqeT=Fvtt54Y zc=ggpdb?0`bBju`qf8{kFnMw!5n17EwSbi=sU*n!YaJ?f69#qmiB@CoXI4gJ^6F^! z3yOK?>=C7X`grp!o~6Px{L!1m53y8G<&DnMG41e4UXf>8&-T`-{Z@xx-OAV5&Y&J zyC{ddyx?K~7S76xQ>ZO}m4*)COHEzoPi;k-HYfoJvpaKWln#mPhpxzp$;r*hH`itR zMkF`g3KN!Q(bj6WG}jrOZX;o6CMF*r_o`egZ;`fTsTddG1=UpeU4YHnY~`p_SfPEZ z5G@!)h;A2{OqBh3dU5m0no&U9^toa|?)pbwgf}&Vf+MA~>aLpiUWJlEpn1qV%4c5< z4gb9q4MKOqa_qH&&*u`21M^m~nD`2yfuvVtv`6CBsinhaVE76c4}UM~`cn^{i9>7< zydA7D+CMthtOet3TLruYDO~>#xFp!_%T}e7E*s2M4Z2bbCSuOYOO@o`fSlAkhQ)vs z^W89%dcjy6xfN>huiuenE~UgU%2OmLbJ{7m_(d(g=3|&8sJ`~1JZzD1_MGV8>xHx~ zYc&KJ7}}kHfNagYf2~Mi)Ddi`%ruG8g~8>366`{aCrAC+PHb`PZ0*CH5#SURBV`jAL)#mRTwe#?QY> zNRSKXNH)m7EpHw(zSmhhcUIHK@kv%gw7MDyY7U#oqW~$4CQfQx)(z`Y9JrC3q{C0j| z1rX^GVE_eu1Nni3p^4s!J_Yxj1`T%hMYf=K!}sTnTixycT=af2{&N*_Hr#W3c-ip| zV!(sv%k%RfVICg5VlOTMB5vG~Y5@`Fy`ePfTC^@&f~_nB6V0rE}LyxHTV{T5e;ia0$cWsrNMMS zgKt{d-aH{%a^m^|JDIHkuwtrJTNM)+d?w!(d@=1Vwr#%8aHb}lH;XqUe2=kJJ5V10`ZXXvqYqQfB zlOu&nnCHL0U?Xr7TI=E+rQwDoi$rjPKP1oquxLs14^)iz_0=QZXFp7vhfvraza5KH zbd@R!oCOLn3Z0uPzX^M1Q&Ug_Zx>dVy3>y$5o9n*6trP0b7Bp7-tYR|;WZA($QPU@ zKJxst)dXB_ROl^t-$sO7jPj3uQ$-oec5MFDkhL0KfJQrd%Zrq5A_ha6lWjHL=g5NWq(=F3PSvT2-_Sm0E%vI!mViBvg<D4K9_ z-Lf-EG~A7pjkk&kO(UG9(_i!&5IgsmoQ!MX^hdc2RA1nyD&ZI!<^iPUg?Rl#oxzB;sk)CJ)@!c4I+xV+rPBN-bJtwMl_#$Zo>&#Kv8~A#q|sf! zB=%zTjD$1PJZa>MC@4b090Wwe+-pL7f1jI!kFUA=g^(iAnW~wzYxxCu#OHVdLOg*r zS#jc}nwGBk@OT40V~Wq9V6EISz=j@`xGwWp8Y(clYx^^GpV{! zygTCZp$bGmD7w>;GxL;h@_ehiUvDV<~M ziwAaWg6#OeEEO8#jZ}g)4y#cz3(~>NL=f#5J>KzJ_Qgjp3QB0qSP1zQB2n5dg`Cz+vt@-7N33tg3Gw6f@BkSJ@ZO{dGzC{p4 zXfTQ;keC{G<*Hd}CgfZ#@5gc!YABseJyA?8bxY{h=29T~T3 zLXPA1$;>8^6;as3Y!2clW#R_}s7#vxGWo-oXez@ozm-sC)!zk_pMm2C{CM8~ZbV*s z^NiP00|O~D9bQ5U9ju2$peMxNKw?@$5{}8SI~wmK$hq52N3~~kz;Xmf#$1*W20`!a zoY)`Dm}F4}pG7>Pk)1croMtLbcYxw5U6QVIs)Urs)FUv^&1~&>YAeUiOS7kxn^ph| zpn}lQ)6-`98je}w&DXyeu<$I&TqCFH3O3%RSiDoJzKg`uWFF^H95?axv|q?xt>YiS zI*sOd=iU$!k1ii$u;T&iR(LWEXI_RTh+b4FW{H61Hr?XjDuAI6A=kt_k}4RW;y#8s z>Jy@yL!0o@V>EH5#FqwV1q{-RTdm^7VnM%icNs%9Wzw5L^|h7(?`Pt+6_2CYqHlvQ z*H$3kekZvGO#EZPhgjJ+nt-|Pybz@a)Mug_!w8xhqI;}^$BCaoPX}>^=cOmT&hOuC zNdHkhG%W`m;PSah#&J$fc&)*U`3y+&>9E20zQ(WInYpbfJ+P@rBJXlnaIkU4%bE6< zL9_R+77s-%Mfl~aRw8abWc#4LE-pc_5@yRq9h2S3ZI7NHL-VC{^mMpuO%D3vaf9h; z@OArqjJ{w~)<04?&TAoqVHiMUVh=YQDw`)*oKkDcHy%sS5xAKuQ455rNJu|XL8LkK zyl{#%O{E|{?y?>X#Ek#yT*;XBH?&SO9eYB#4~0C=?)m9ydOD{@w+E&-);tQ|npXxB zKgyblG}eY9RzvKUWdkQndiq(~saPHB8;S*aDXYzWqFx#%#gbm_tDbAONmCqi8tLf_ zP7w-H$6UvQ!D`xWTrL!C5dUzZ!QvI1nM-U7V#uyixRYM0E~`Bt%TX!zn6SJrSp0E{ zlMaYIc`?aNoXdn9{)$CAbj})zGLsif!YyL2ImKegqrh|3Id#yWWzH}UvE*I5sfdK* zF9_d8*o%y!7bn6+UB^KX*5Hm*-J1$77?R1Y z+*|1!OfF#$8Ehp(XecqOfq>Js0q-;nuLyt;|C?O6x0CGYr0|S9dHgX*>Wdr-c^j-| zw_dG*_Df#wk7jF&Nrt`B+%PD3G?O!=I@9MLC98kyHg+q2s48{KP$HyOnTyLz43kcJS+Q^iV6ONEB}xib>dYG#-`;NQG*fty;1m z8{ZIl0eyd72ZS`06)xip{n5AAByVcgqs1q+>Z*}h`xdcS-Tf_Qby)l$QSS6dK{+*x zVEM27Cr?B>6)m8{mrqSqdgbpi#SVNhf*9>85lP%XovBe*7Xo(gV+cCGmv^d@o8P@e zaIDBPg7YAwsd@#=SG-E<=#Nk*Vz}K(#$w8+QZ*n_YW89Y(5OD;!!s-svg)|d@<}!G z4w2$K?JiW0g@&{zba7}4RhQ!$9dh`rdb$qf*gbd`gm{j3hdz2Zi{2BZrqzQ%bK;0p`9$2aH=SmLJn_&( zZFKL`;tBOSJ zXs_GLxjBMBDN3B${r)z|fA*dF&fQl|8fNfm^mJ?LhRx5VL2xRp>w;YP*?D+;K3{I_ zZPHx|4@d5_%fpuFf3A%g&;x_(YWYR~(#yBL>X&k0Ho<77hUP1G!cBhNH);cp$_xxHUtix&OV@`tD&I%bWG~bo<$>1MFEE5H8;MS^U0{(o zpNk2Suq!tH-U>(->pg2-M$y2IA-mJ$SH?@K>k;Um61@2J`_glDFoP&B(leG@RN`52 zmHxgPBq9_^~@poFg^;oh^Vt6wwYKRr$^_A5(re-2qfrSubGLD8I zP>aks3zS=J7bE3?_E4FN9J1}#uZ)h=#Iic>D_uWEtuU6PIg=(^WgHcpE?2(}3tNuz zqU2JL(V|hV5*yS$U^$Ds<#8NFYp zId6Lj&C}Q*v?f%RMYm11br_ne=xbLolZB)~4>J@FE(mZnDoN|Y@u!K13lI5RxbMF| zK30aR?S6gVAJ+A3yMB1rx8`j;IaqJh;{|^tH(5%jQkQ&zKaxmRy@$sqU{~3p!Q`!Q2$mCRU?gzK)zs?jzg@L$ofjVnr z*0)a*S>E(|7o}eDINqb$c+nAlx4W)g2G56r zYKzK5BnoKqJR&^hOa(t1)Uo*1$d45MQ7(v!`Yvqp>K(*xnIg*HtEPsUR91zf>b^n! zA;ld0EDQOO81KIi(WrOHtX!(&713<&!|CpX{#IJ;>1>-ay2R<}wuquY9>dJc+V-H2 z6);cl*@~#$5aets^!SnQm-7zYHGOw@eRhv{N!A{iKC!={_Q4#Apu4FmhmX z`aYk39oex3hL^O1dsZVx{ha0Iu_>g~9Sta^kK>4c#tbCtAISLk-6Pp#vWHDunfxFwrvTI(x9Gdr|!eeN9a#r65-E z%?@quaPJKq)r6@4;v%1Im{w7(fB>u8RxHXt+1RD5htZ$PheDNYepJR%R*|GkkRrC! zRk6CfymGYmrG3P-pIZQ=O!QG^SuMOXk(cu zl7xQCa9is4PamruP<4gZM3x5nBMx;$HC-7*+NC^`u68#yF=Ymp=m*w z#>)&8Hs|J>?{NSfW(C%t$}Edp2`clzyNl9C`lND#-nDmVM#g68i`p_GxUc`I{-n#W z^d*7?1QaFmpCS|gePZ(eqO92R)px}GADx{AP@K({_X+MAbO;1@cMl#UxJ#13eFzM0 z!Gi{O2_77Ry9Rf6LU0L#1(%)dci(qO_U?D@ZdXms)YLP-bNY1G)6Z1*`5(EMZytA{ zM3&}qBqfYqb8)HGx=e%B`ir$2@f*CJUnppcV@&kE4mp?+Sk}(A3zHsN`U)&9EyIQx z4lTB_RbaW)`%LCD@7 zUk73f_mLyQU@40GJRxHzvKC!hCDKn#wIlr)@Ql!(UNv@ZL!Kn>m9Q$WCa*C7i=1h2 z^J%~n^^Jy!gaY;?|7VUbr%cpd0=LKglg24IN>85m_#Cafjo@`~Rv6QkW9Uq?1fzqm zMi~XG=9yDC(q$PkdyoRt4Tv>&0w6-d54f3%u8g7mty_|1JcB#BowNd7Ed>F`n%*^4 zHC0tkJ}w^q_6lF8@Lnj&yaj*JQFFwjNoG`|NY22cA&pYmRb{5N>0YNCeX6j&>s(NE zH+)yxQVKHaKCxmxEWUtb8$|NHrBxGJS%oV5Ex;tq-$iIEZrlBwV|#degiTHScoOR()&KrDBKTu618G4-z6?yaS8xrrX(bvXTRw z!rM6gB>G_r%^3fv?&=)#UUocZ^b{)NOC&U5e62oJ0AjLynFwrAg&T0O-2^qbx7+&G zf`77~|HP(*R|d8sBQr%mkCW0WJ@xg}Oy%M1hPHz#Zo={Tft8DOSiQ@IUBT(AZSS z%lwGFd41jDReqO{%Z0Px#ksRkS4%qd0uPTSq2{VZV&{o;pt%Rg1yttqh;jL;xZ;&QD?ZXP%vPIr<;5FByj*zE)XW0Ym&mWB)4K+ zlnxvc45%`fH?*)zaPWA;(xmW%tl6Bn8>+p$orQrt4u+E5ryWei6W^^kNEQ*AYHN9) zdlSK<-SmG$D$`6lNgIqCG>If>?&H^!MWc^GtWQHN-3U-XQ_n{Lm@Kuc6r-^3$Qmf! zus~cxNxLN-SBuZ`YI$XeD$dz|9UTU+U>kFWFQApCA zNY2i(luP?*4aEPh}8`9g8ycLQG=vpSv9(s5h2mN^t1~*>8@8FxuWeYFN@gM_DbtsVFkdrZ*rSTn-Qv5 z@R#$v*2`)Ra?s``{j5ExwBEL%-Q|T2W z_*}31T#+LSvb?z=_IUs)ZEg?;uSirY@&G%j0mt^=kpg+HxX60Q?{VD8=cq^PJBXKj zSXb|rT}yD?V~cXzL#W5ju6up%;e-Wl^*selAj>(c4@`#_7DF2!%&pF9iNDIOLZ8xp zhzNO+yqeI$wy~2)HxVC$Tp$gF`@cHM^;e`ZiAhb#*$zMwwMF2})Szl+MVIW#C#8Vz zC}drwyz5IDO-?`>>lJICm>S*lAhjw%nGu?B)SNSFm%}z2qFwjjy|?zb%N(gy%E(>J zi3slS*Bkq$G1tYjfE(D1Y0dbZB+2zwbW?Hze8$Q)y{*#!(}= zWlL*|?ByOT^O*Ho1oRdSid}>&4P)MG5K~Cow|e~C*&KIi3A>A!EnpiRaapd5_}1(K zo@uJTF1uK0%Yv-_d@UyOBi(0FXy2BffuFlQd1R{{ePe*gVJiU4^hJ17N&W5SOgahT33lmPX| z?3hD@1vI~mK~V37n4K~V5(oU9MQ zc7&yx0e4%%tt&6MNpHhAsxaU>WhzXbh!+uuCsQaHo;IA86fiT@i+59ON`~gjJIoN> z(mX}WrbmUX0z}k%X;9BDkulh&kS}kuPT7Ytx?N5M0V(iI3}daAY1x_)$i-@w@_G6A z(piTBHpq6L^V6!r^}C_QF6cv`oA95xUE`4&2-M>YmfQgcI>eZYj+BFx?x1t>!!Gd6 z>uP-4pZZFp*i*h+2Mf${r(sp=5G*!#B`e8Wh$>|F7$q&8+|LII8q`TZ_JGSeNU60K z>rzbQFkSeyQ1jD^@N6okeBsnO%PN%Es3O?Sd#@~JB;#?4cS}-Ueg{9=qP<@BC{}el z+=WGZQz?Y6_KjxW$k;Jof~5qPQ=Zd{bp#9p+g0V7UZ!t^aR*ZlQ*eJy1_x+;-Z`Gc zT}ZX}?F|=}Hs`i6v@Dq>u4oWETkJXoUGRlVD9iUn3mG#nq4y4j-w`n8B6(6%5A_(t zHZC5(Jv>L3377Fb(uw+bOWDkQl4~3vVwL*bjZ!%#ywDMEzf%@n zlhZ-UtAm>nU3g{3+-L;O%s4un!hFGg-BD@dbE-TNUWkZ8{fs*n$#F7g^co*1o|3<| zJtknAAX~F-Wq^(14k9sC^E0xIAF;Ns)((5jJrrli@V?coD5Qg0%uwJr>{7 zFmiVKpbCig$0)7go}8}J^_;sWT$F`D0xa-1WN)QjawC0>dU}r96UzQt?btcPk>~8~ zf{Odfy91;y*-xffXc%>zo_a6u(S|wHQ2b`^KWS}qpx^aej~xUzuKEpfT5bf0)+vm? zW_j7sPTe1XCNJ?!>yrc_MNO1xgMPyjuW*WFQIx4gTH5!Q_%BV1db64i`I;C^u z?a=7CI`};KKo&rfaDX-3TEIF9`4ARRghQFRTS41>CJy0Kq6*}HJ9eAOFrLKt2 z7}EMfLOTb!M9f*scgCX^whpH_K6wKBWYmb6aFf^>8l3Ad>Dv*$*tw_<>*`||Boczh_fPzoJ91%j zVSfsfB?^igF!GOb6;&vz=s#!9SP2cUl0mpyLO6ADd%cTTsTJcSS9ZM5t|b2O0oMx ztfMma4L@BDaoXjpW3dO2B06a{&zarqren=Lb$mg~bYS8HDwwRo`NOqW4+TC$@)Sp^ z!wH=Og>A5Uj2*j0r}LFVD;}>@WI4_ ziv*}4n|$kg(mJw6aM372!P>oqs~N4$FoZN&Z8?+d<_ja12YvIJ+wSFFlB|7sQ6XDH z^?o@7Tkfqdh?dFItKx7AlS#g|CIxm9G_B(aW))(~Vm@CBR75lWNt(EQ%84m)%{>9T zQYgVUMx-16#SMTMDBWVM)L7UG>6hi4O`Nndo{FTh0Vsl|oTo zlgbCy*>wlE2gK3z{8s6L3pF5Ur>bWA_%!s@rooDejro#-*n?h+KAGp`B>BUQPatHs zW>E)KeW>uh?g4Jp(`-TvLOJ^$gMDy)I&Y8xojVg%2t>qwkWx3^@0ig`GACn-5N+~x zqg0^JbWR0YCh7?I%npAEnYA)%lRtECOTp7`b4Y!Mt4g#oEokr%E-ibI8eLO3ym*NJ zfpIiRdUlBcBTp4y2QqGiGY6xfX=li+vFf5L0+X1S601}y!?F_Vt{88l^Nrl$P3ccM zxqh3-*XhUZp`B!ToELSu0{Zl7-m&LfiDYG?Ti?Wd=0shJthUP&I%I`L*J89$?nbHf zXl_)$)_dl-Sog7X8W*B+quO*9`b`9d_p5fu?1av66}%2s6Ymbp7jyYBXEl;o9FXNd zrU&Cs`&I4J#;Dz^n5A2E}8fimJK~kL& zFuVh+@|6mbTm~C7l$2~WGupnr`i6Oyj(vViCD(kDdjhMEQ$#HoiQ?yApWI}!8}VIn zF}))X-5!v{VMIOdRLgkjcqUN|SB<@Y+BO7X{$X6NH~NAq=5wm6-uF;lOxmo`8m)y0 zLdkBAJ}GD-4Lh%BM{$Grc9HxM>wuEc2Lw}30M2IuPjndKPi87UWS*!AYOVL_U9*X) z^0MJ9d1nr0it(-0MMk*_ZqP`3Y_XK2<)RG7Zn+DRzW zGUg5S8~F!5TPgq1?$lime4`b8DNvsf)dW2{+NX(5o&!*k1@&!^qYsl-fQVL#wX0|J!pvI4% zHAi8qj&Cih6gTKHBO^aN9x%)|X2wTU6Oy_pfDakBHGe3_GSkgg#;k?sKzMP9Tiexai1!{VUq z+dyGE-f*e4Ma3vfevvTZ8zB21E3J9?wGqb9-U*C1RO6p9$ zK;Y}HxjY;>JZvfbp@iS@Aw0M90C@KL?wae4g6 zH^7KV98}7qlTB%w%H1;JN7#{-Nw$WAE&FA?Mx4`2??eiMWm5VnjRUa0kXoNG6T>|- zR#}LgMqU?ZJW@GqN;^~#&KQh+w-rmZ7zoDf5E)j-iLgBoxV}jNSP2fx< zQLB32jPN#Q&A!rPYE*XC3A zbj4&Cnz@a0=d7B~AQe^}{08dv9VY*)_ zYKMH$JEnWx`IGsBi;T8<*OoV{3?jP{Imd+`U%>NPyM77~%h2KOoZAn56*YUrw_QNT zVsU;9x$lk0QUe&y5wW)%7h)2Z9GB@Zl1ra7T+K0>2!##3OHXE%u#;> z&~ip6&BGYPj)QAme3v;Z>}xG!D>f{#ng^C`x3N z<&I>hcVS#Fr=#T6z1>TvocXit{(N&+8q~HX!@E~Jg#-q6Ma*5gDa0F;OnL>**~J0| zo0jSuhBu#h7h4lL$-_}+UQtaPi1}O{e7~GOmIL`np3DRG77a_pN5qdRNu~mWHmYEd zc$$NWJKxjHYQ44fX&|hahjit1^?Kepz)juW*MK6ne_KjHo$3c#F=Q0oii*4XPH zHJS?cZYvAuaUC%@Kjb=G3)k~*@vx{DL58*Y3_hf-@s5IgpSqRGiz~mOA){NMK6r_Z zvXI7`f5q$+9`(B1rF8j7$$&cfGcz6opOP%l8c?dmx;^C-qMv5R@_C%R(6e}f^*9RoBiWP2zs8w2q*#UIw4TC(7Zd3 zh@vn-k@uBSsVLS?nZ63n4sg+7iO@K{cCyLSR&dhc%+ZZPR`nS-0+LG#L7r~Z44g8 z#RKxI<`lz}7$_;mA(0CD(1*OnocvGC{(`vbJ}t=cn`vc+C3Z`wx^EYg!@8O)J2LvN zfgzs~Dy}AM6yZhY>R!c;kR84 zL1fD+Veb?VAA&9XywF-sw@dX8p&OBih6R~+s`rJ1rkk*>2YX8&f*!UYanB@ZONRN0 zt;4NNNK_SnqDCNp!K`JJ5t#jyRq*tnf1!D zfd1ro42dzX1pT2MZ$fh_FYRP0QZfZjiz|FdSEj|u7}ajTdXs9{p}Ag6vrL^nneId9 z+ryHKuKB^;&hW-~t1J~rvwzPGN7+K)wjC830=HRzjjn^9OKmOWtNw+to#1B!(#8y! zcTC8ABFE#=?Cfh(NI?S6DT-^;;1pkcV{*wgdx|K;<}E#;eLRaGnFU$gD~d+;wbjNFk3w0g4kM!hQBt(Yjv zK`Wyn-vQK?@Pi+hYE$cL$XZ|It#NZF=%g!IRX1bql+Qc2os=RH$Ie;^$k!FggmG4K zd^^jbN>F%f7H!$ChB{KcteXR-028snD)HsQ`SAEoY=W^LhGIQVtBA6Z&Zl_uQ-vvL_YDyl?LFq zChdeqo3b*staq*e5~5{pJ^nMi`}JqvSPdpjG<^8#7Hw*Z>uAc!CVAW*Rwxk0KJVVj zgfQK2D_AE9hZ!s=p>JD<#(H-&+5r_6i^3# zYSTC{>v=Ah)-U*(c-fr=k$-ydIQ7PjMqiA&{XzYPsi^4kr=DVLN})6WZU*NrWhr}X z645Sph9yE}2a>gB3=7&TX&82192WUb9UZ2QlG%UK^KrI27I5L6ka}5YD|S5q2(J6N zx_(`@oLmxm!ZY20XXQ<;k!#{Y0-0IZcr+p zPs-M`4$n}`EaFWx!Pt4!Yf5Ks^7&?=J%1Ql%vZ#LJcu@SwF*gNE{y&fG9ZbzEU7-nr z|J|_zL@SL9XaeF2RGOUdHy}2Y|FF;KOly2{yN}po>Ut+7?_DaCQPpH zDZ^vP;{@Et5&AV&>GS0aaDgx|BVjNwfZrU+fsWpPIbv^VZwh8{w6nB_zO&lfng0d) zYxa^dX1H!ad$f-G=gfnAc?|mdaKc}JUqjCC>95m1&o>=N2?JwM1Op@Znug7?X!P}{txN5Ke3P11AfV5{MBlGz3BfH_8$wwpUB6V+rLkV_e}o^ z`Tv{FkI!BG&edl9SKP;0;D7r2c(MO|svXPxdt7L0xrzb;^sf4A6(okCMTUWyGJ&o` GF#iFCwRTwm literal 0 HcmV?d00001 diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index a2d8462f7..8616e271c 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -1,13 +1,14 @@ { "name": "pipet-code-agent", "displayName": "Pipet Code Agent", - "description": "", + "description": "A AI CodeHelper", "version": "0.0.1", + "repository": "https://github.com/google/generative-ai-docs", "engines": { "vscode": "^1.78.0" }, "categories": [ - "Other" + "AI" ], "activationEvents": [], "main": "./out/extension.js", @@ -22,6 +23,18 @@ "title": "Pipet: Review the selected code." } ], + "menus": { + "editor/context": [ + { + "command": "pipet-code-agent.commentCode", + "group": "PipetCodeAgent" + }, + { + "command": "pipet-code-agent.reviewCode", + "group": "PipetCodeAgent" + } + ] + }, "configuration": [ { "title": "Pipet Code Agent: Google Gemini", @@ -38,7 +51,7 @@ "type": [ "string" ], - "default": "models/gemini-1.0-pro-latest", + "default": "models/gemini-1.0-pro", "markdownDescription": "Provide the name of the model you want to use. Choose from the [base models](https://ai.google.dev/models/gemini) or your own [tuned model](https://ai.google.dev/docs/model_tuning_guidance)." } } @@ -51,7 +64,8 @@ "watch": "tsc -watch -p ./", "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js" + "test": "node ./out/test/runTest.js", + "build": "tsc -p ./" }, "devDependencies": { "@types/glob": "^8.1.0", diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 5dc517478..9523604f2 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -17,6 +17,7 @@ import * as vscode from 'vscode'; import { GoogleGenerativeAI } from '@google/generative-ai'; +import { getCommentprefixes } from './getCommentprefixes'; // Provide instructions for the AI language model // This approach uses a few-shot technique, providing a few examples. @@ -49,13 +50,67 @@ are offset 1 from line numbers. ${CODE_LABEL} api_key = os.getenv("GOOGLE_API_KEY") ${COMMENT_LABEL} -Attempt to load the API key from the environment.`; +Attempt to load the API key from the environment. +${ CODE_LABEL } +UFUNCTION(BlueprintCallable, Category = "TransparentWindows") +static void MakeTransparentWindow(ETWMode Usage); +${ COMMENT_LABEL } +@brief 创建透明窗口. +@param Usage 窗口的透明模式. +${CODE_LABEL} +virtual void Tick(float DeltaTime) override; +${COMMENT_LABEL} +@brief Calls super::Tick(DeltaTime), then updates world bounds. +@param DeltaTime The change in time between two points or events. + + +${CODE_LABEL} +sendMessage(request: string | Array): Promise; +${COMMENT_LABEL} +Sends a chat message and receives a non-streaming +{@link GenerateContentResult} + +${CODE_LABEL} +export declare class ChatSession { + model: string; + params?: StartChatParams; + requestOptions?: RequestOptions; + private _apiKey; + private _history; + private _sendPromise; + constructor(apiKey: string, model: string, params?: StartChatParams, requestOptions?: RequestOptions); + getHistory(): Promise; + sendMessage(request: string | Array): Promise; + sendMessageStream(request: string | Array): Promise; +} +${COMMENT_LABEL} +@brief ChatSession class that enables sending chat messages and stores history of sent and received messages so far. +@param getHistory Gets the chat history so far. Blocked prompts are not added to history. Blocked candidates are not added to history, nor are the prompts that generated them. +@param sendMessage Sends a chat message and receives a non-streaming +@param sendMessageStream Sends a chat message and receives the response as a {@link GenerateContentStreamResult} containing an iterable stream and a response promise. +@public + +${CODE_LABEL} +virtual void CreateClassVariablesFromBlueprint(IAnimBlueprintVariableCreationContext& InCreationContext) = 0; +${COMMENT_LABEL} +@brief Implement this in a graph node and the anim BP compiler will call this expecting to generate class variables. +@param InVariableCreator The variable creation context for the current BP compilation + + +`; + +//Code comment: (generated) +//@brief Generates a code comment for the selected code. +// - Gets the API key and model configuration from the local user configuration. +// - Builds the full prompt using the template. +// - Generates the content using the model. +// - Inserts the generated comment before the selection. export async function generateComment() { vscode.window.showInformationMessage('Generating comment...'); - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'models/gemini-1.0-pro-latest'); + const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'gemini-1.0-pro'); // Get API Key from local user configuration const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); @@ -91,8 +146,10 @@ ${COMMENT_LABEL} // Insert before selection. editor.edit((editBuilder) => { - // TODO(you!): Support other comment styles. - const commentPrefix = '# '; + + //Code comment: (generated) + //Get the syntax to use for a comment. + const commentPrefix = getCommentprefixes(editor.document.languageId); // Copy the indent from the first line of the selection. const trimmed = selectedCode.trimStart(); @@ -105,6 +162,8 @@ ${COMMENT_LABEL} } let commentIntro = padding + commentPrefix + "Code comment: (generated)\n"; editBuilder.insert(selection.start, commentIntro); + //Code comment: (generated) + //Insert the Python comment into the editor at the location of the selection. editBuilder.insert(selection.start, pyComment); }); } diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts new file mode 100644 index 000000000..fc6650e11 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -0,0 +1,26 @@ +//Code comment: (generated) +//Given a file type, returns the comment prefix appropriate for that file type. +//@param {string} fileType The file type to get the comment prefix for. +//@returns {string} The comment prefix for the given file type. +export function getCommentprefixes(fileType: string): string { + switch (fileType) { + case "python": + return "# "; + case "javascript": + return "// "; + case "html": + return ""; + case "css": + return "/* */"; + case "cpp": + case "c": + case "h": // C/C++ header + case "java": + case "csharp": + return "// "; + default: + return "//"; // No comment prefix for unknown file types + } +} + +// TODO(you!): Support doxygen comment styles. \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index d2fcc7bea..500335551 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -16,6 +16,9 @@ import * as vscode from 'vscode'; import { GoogleGenerativeAI } from '@google/generative-ai'; +import { getCommentprefixes } from './getCommentprefixes'; +import { get } from 'http'; + const CODE_LABEL = 'Here is the code:'; const REVIEW_LABEL = 'Here is the review:'; const PROMPT = ` @@ -44,7 +47,7 @@ There are duplicate lines of code in this control structure. export async function generateReview() { vscode.window.showInformationMessage('Generating code review...'); - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'models/gemini-1.0-pro-latest'); + const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'gemini-1.0-pro'); // Get API Key from local user configuration const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); @@ -83,8 +86,7 @@ export async function generateReview() { const trimmed = selectedCode.trimStart(); const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); - // TODO(you!): Support other comment styles. - const commentPrefix = '# '; + const commentPrefix = getCommentprefixes(editor.document.languageId); let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); if (pyComment.search(/\n$/) === -1) { // Add a final newline if necessary. From cda1812c2a369a401872168734937c4fc6c22604 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 3 Apr 2024 19:53:17 +0800 Subject: [PATCH 02/37] Add license headers --- .../pipet-code-agent/src/getCommentprefixes.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index fc6650e11..98fa22341 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -1,3 +1,20 @@ + +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + //Code comment: (generated) //Given a file type, returns the comment prefix appropriate for that file type. //@param {string} fileType The file type to get the comment prefix for. From 05456814aeab3bcc907b23b9065119a9f960b6a0 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 3 Apr 2024 20:06:55 +0800 Subject: [PATCH 03/37] fix license copyright years --- examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index 98fa22341..fae731bd4 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -1,6 +1,6 @@ /** - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c74b08ddaf2f5093a33292d061f91c231107a034 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 4 Apr 2024 00:42:51 +0800 Subject: [PATCH 04/37] modified: examples/gemini/node/pipet-code-agent/src/comments.ts modified: examples/gemini/node/pipet-code-agent/src/review.ts --- examples/gemini/node/pipet-code-agent/src/comments.ts | 4 ++-- examples/gemini/node/pipet-code-agent/src/review.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 9523604f2..a7d083c27 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -56,8 +56,8 @@ ${ CODE_LABEL } UFUNCTION(BlueprintCallable, Category = "TransparentWindows") static void MakeTransparentWindow(ETWMode Usage); ${ COMMENT_LABEL } -@brief 创建透明窗口. -@param Usage 窗口的透明模式. +@brief Make Transparent Window. +@param Usage Window Transparent mode. ${CODE_LABEL} virtual void Tick(float DeltaTime) override; diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index 500335551..15eb3d290 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -17,7 +17,6 @@ import * as vscode from 'vscode'; import { GoogleGenerativeAI } from '@google/generative-ai'; import { getCommentprefixes } from './getCommentprefixes'; -import { get } from 'http'; const CODE_LABEL = 'Here is the code:'; const REVIEW_LABEL = 'Here is the review:'; From 2ca24f2735f1ec94cc66ea2a3228aafab30f0f80 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 5 Apr 2024 12:30:40 +0800 Subject: [PATCH 05/37] remove binary files --- .../node/pipet-code-agent/PipetCodeAgent.vsix | Bin 93456 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix diff --git a/examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix b/examples/gemini/node/pipet-code-agent/PipetCodeAgent.vsix deleted file mode 100644 index 3b586618b49a78ef04bcece2b163d3e4cc7ecf7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93456 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

ks| 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 GF#iFCwRTwm From 36ea6d05d8955da79a556678988f73a3c2acb986 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 5 Apr 2024 14:50:56 +0800 Subject: [PATCH 06/37] reset to origin --- .../node/pipet-code-agent/.vscode/tasks.json | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json index 067c0ed1c..078ff7e01 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json @@ -1,26 +1,20 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { - "version": "2.0.0", - "tasks": [ - // { - // "type": "npm", - // "script": "watch", - // "problemMatcher": "$tsc-watch", - // "isBackground": true, - // "presentation": { - // "reveal": "never" - // }, - // "group": { - // "kind": "build", - // "isDefault": true - // } - // }, + "version": "2.0.0", + "tasks": [ { "type": "npm", - "script": "build", - "problemMatcher": "$tsc", - "group": "build" + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } } ] } From 285b78aa3ef8330477306e5e7cf738817cc33049 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 5 Apr 2024 18:56:31 +0800 Subject: [PATCH 07/37] Add activitybar butrton --- .../node/pipet-code-agent/img/Gemini.png | Bin 0 -> 9022 bytes .../gemini/node/pipet-code-agent/package.json | 18 ++ .../node/pipet-code-agent/src/extension.ts | 178 +++++++++++++++++- 3 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 examples/gemini/node/pipet-code-agent/img/Gemini.png diff --git a/examples/gemini/node/pipet-code-agent/img/Gemini.png b/examples/gemini/node/pipet-code-agent/img/Gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5c560d576c41abcfbc0fb2f96ae33e3071f179 GIT binary patch literal 9022 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¨sA^+KCjjctH4ZBl39S$*Ndj9o0I`Ck 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 literal 0 HcmV?d00001 diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index 8616e271c..306dd5315 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -13,6 +13,24 @@ "activationEvents": [], "main": "./out/extension.js", "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "gemini-chat", + "title": "Gemini Chat", + "icon": "./img/Gemini.png" + } + ] + }, + "views": { + "gemini-chat": [ + { + "id": "pipet-code-agent.ChatView", + "name": "Gemini Chat", + "type": "webview" + } + ] + }, "commands": [ { "command": "pipet-code-agent.commentCode", diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 67b967078..1984439c9 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -12,17 +12,179 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - -import * as vscode from 'vscode'; -import { generateComment } from './comments'; -import { generateReview } from './review'; + */ +import * as vscode from "vscode"; +import { generateComment } from "./comments"; +import { generateReview } from "./review"; export function activate(context: vscode.ExtensionContext) { - vscode.commands.registerCommand('pipet-code-agent.commentCode', generateComment); - vscode.commands.registerCommand('pipet-code-agent.reviewCode', generateReview); + vscode.commands.registerCommand( + "pipet-code-agent.commentCode", + generateComment + ); + vscode.commands.registerCommand( + "pipet-code-agent.reviewCode", + generateReview + ); + + const provider = new ChatViewProvider(context.extensionUri); + + context.subscriptions.push( + vscode.window.registerWebviewViewProvider( + ChatViewProvider.viewType, + provider + ) + ); +} +class ChatViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = "pipet-code-agent.ChatView"; + + private _view?: vscode.WebviewView; + + constructor(private readonly _extensionUri: vscode.Uri) {} + + public resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ) { + this._view = webviewView; + + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + + localResourceRoots: [this._extensionUri], + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + + webviewView.webview.onDidReceiveMessage((message) => { + // 处理来自 Webview 的消息 + if (message.command === "sendMessage" && message.text) { + // 在 Webview 中显示接收到的消息 + webviewView.webview.postMessage({ + command: "receiveMessage", + text: message.text, + }); + } + }); + } + + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.js") + ); + + // Do the same for the stylesheet. + const styleResetUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "reset.css") + ); + const styleVSCodeUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css") + ); + const styleMainUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.css") + ); + + return ` + + + + + Gemini Chat + + + +

+

Gemini Chat

+
+
+
+ + +
+ + + + + + `; + } +} +export function deactivate() {} From 5c478afe8e1bb28e4ca77abdc7d6d63d3d40385c Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 6 Apr 2024 00:51:48 +0800 Subject: [PATCH 08/37] modified: examples/gemini/node/pipet-code-agent/src/extension.ts modified: examples/gemini/node/pipet-code-agent/src/review.ts --- .../node/pipet-code-agent/src/extension.ts | 214 +++++++++--------- .../node/pipet-code-agent/src/review.ts | 56 +++-- 2 files changed, 137 insertions(+), 133 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 1984439c9..6c68e6b63 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -60,7 +60,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - webviewView.webview.onDidReceiveMessage((message) => { + webviewView.webview.onDidReceiveMessage(async (message) => { // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { // 在 Webview 中显示接收到的消息 @@ -68,120 +68,116 @@ class ChatViewProvider implements vscode.WebviewViewProvider { command: "receiveMessage", text: message.text, }); + const comments = await generateReview(); + if (comments) { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: comments, + }); + } else { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: "请求失败。", + }); + } } }); } private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri( - vscode.Uri.joinPath(this._extensionUri, "media", "main.js") - ); - - // Do the same for the stylesheet. - const styleResetUri = webview.asWebviewUri( - vscode.Uri.joinPath(this._extensionUri, "media", "reset.css") - ); - const styleVSCodeUri = webview.asWebviewUri( - vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css") - ); - const styleMainUri = webview.asWebviewUri( - vscode.Uri.joinPath(this._extensionUri, "media", "main.css") - ); - return ` - - - - - Gemini Chat - - - -
-

Gemini Chat

-
-
-
- - -
- - - - + + + + + Gemini Chat + + + +
+

Gemini Chat

+
+
+
+ + +
+ + + + `; diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index 15eb3d290..bd14e0f97 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -24,7 +24,9 @@ const PROMPT = ` Reviewing code involves finding bugs and increasing code quality. Examples of bugs are syntax errors or typos, out of memory errors, and boundary value errors. Increasing code quality entails reducing complexity of code, eliminating duplicate code, and ensuring other developers -are able to understand the code. +are able to understand the code. +使用中文回答: + ${CODE_LABEL} for i in x: pint(f"Iteration {i} provides this {x**2}.") @@ -45,23 +47,29 @@ There are duplicate lines of code in this control structure. `; export async function generateReview() { - vscode.window.showInformationMessage('Generating code review...'); - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'gemini-1.0-pro'); + vscode.window.showInformationMessage("Generating code review..."); + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "gemini-1.0-pro"); // Get API Key from local user configuration - const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey"); if (!apiKey) { - vscode.window.showErrorMessage('API key not configured. Check your settings.'); + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); return; } const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({model: modelName}); + const model = genai.getGenerativeModel({ model: modelName }); // Text selection const editor = vscode.window.activeTextEditor; if (!editor) { - console.debug('Abandon: no open text editor.'); + console.debug("Abandon: no open text editor."); return; } @@ -77,22 +85,22 @@ export async function generateReview() { const result = await model.generateContent(fullPrompt); const response = await result.response; - const comment = response.text(); - - // Insert before selection - editor.edit((editBuilder) => { - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); + const comment = response.text(); + return comment; + // // Insert before selection + // editor.edit((editBuilder) => { + // // Copy the indent from the first line of the selection. + // const trimmed = selectedCode.trimStart(); + // const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); - const commentPrefix = getCommentprefixes(editor.document.languageId); - let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); - if (pyComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - pyComment += "\n"; - } - let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; - editBuilder.insert(selection.start, reviewIntro); - editBuilder.insert(selection.start, pyComment); - }); + // const commentPrefix = getCommentprefixes(editor.document.languageId); + // let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); + // if (pyComment.search(/\n$/) === -1) { + // // Add a final newline if necessary. + // pyComment += "\n"; + // } + // let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; + // editBuilder.insert(selection.start, reviewIntro); + // editBuilder.insert(selection.start, pyComment); + // }); } From c875ca4abf4914e45bcae292c08cbf6c7c1d55f2 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 6 Apr 2024 13:39:04 +0800 Subject: [PATCH 09/37] add .vsix to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b25b3fda4..a03ec1b99 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ **/venv **/.python-version **/node_modules +**/*.vsix From 38ce18f08ec0e94fd401ac33c90577e6d4e4f821 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Sat, 6 Apr 2024 13:42:22 +0800 Subject: [PATCH 10/37] modified: .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a03ec1b99..306899526 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ **/venv **/.python-version **/node_modules -**/*.vsix +**/*.vsix \ No newline at end of file From 2f6db10ab005f95bda9312c9c71437a367b8dda4 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Sun, 7 Apr 2024 01:29:41 +0800 Subject: [PATCH 11/37] new file: examples/gemini/node/pipet-code-agent/src/chat.ts modified: examples/gemini/node/pipet-code-agent/src/extension.ts new file: examples/gemini/node/pipet-code-agent/webview.html add Chat stat --- .../gemini/node/pipet-code-agent/src/chat.ts | 44 +++ .../node/pipet-code-agent/src/extension.ts | 253 +++++++++++------- .../gemini/node/pipet-code-agent/webview.html | 143 ++++++++++ 3 files changed, 338 insertions(+), 102 deletions(-) create mode 100644 examples/gemini/node/pipet-code-agent/src/chat.ts create mode 100644 examples/gemini/node/pipet-code-agent/webview.html diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts new file mode 100644 index 000000000..068d421db --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -0,0 +1,44 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +export async function generateChat(prompt: string): Promise { + //vscode.window.showInformationMessage("Gemini thinking..."); + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "gemini-1.0-pro"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return "API key not configured. Check your settings."; + } + + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel({ model: modelName }); + + const result = await model.generateContent(prompt); + const response = await result.response; + const messages = response.text(); + return messages; +} diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 6c68e6b63..4d6dfc67c 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -17,6 +17,7 @@ import * as vscode from "vscode"; import { generateComment } from "./comments"; import { generateReview } from "./review"; +import { generateChat } from "./chat"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -54,25 +55,24 @@ class ChatViewProvider implements vscode.WebviewViewProvider { webviewView.webview.options = { // Allow scripts in the webview enableScripts: true, - localResourceRoots: [this._extensionUri], }; - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + webviewView.webview.html = this._getHtmlForWebview(); webviewView.webview.onDidReceiveMessage(async (message) => { // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { // 在 Webview 中显示接收到的消息 webviewView.webview.postMessage({ - command: "receiveMessage", + command: "Message", text: message.text, }); - const comments = await generateReview(); - if (comments) { + const chat = await generateChat(message.text); + if (chat) { webviewView.webview.postMessage({ command: "receiveMessage", - text: comments, + text: chat, }); } else { webviewView.webview.postMessage({ @@ -84,103 +84,152 @@ class ChatViewProvider implements vscode.WebviewViewProvider { }); } - private _getHtmlForWebview(webview: vscode.Webview) { + private _getHtmlForWebview() { return ` - - - - - Gemini Chat - - - -
-

Gemini Chat

-
-
-
- - -
- - - - - - - `; + + + + + Gemini Chat + + + +
+

Gemini Chat

+
+
+
+ + +
+ + + + +`; } } export function deactivate() {} diff --git a/examples/gemini/node/pipet-code-agent/webview.html b/examples/gemini/node/pipet-code-agent/webview.html new file mode 100644 index 000000000..f9888ebca --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/webview.html @@ -0,0 +1,143 @@ + + + + + + Gemini Chat + + + +
+

Gemini Chat

+
+
+
+ + +
+ + + + From 16c05bfb0f7d75fc8013ec214d675166a59c9c37 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Mon, 8 Apr 2024 23:55:29 +0800 Subject: [PATCH 12/37] Add Doxygen Style Comment --- .../node/pipet-code-agent/src/comments.ts | 152 +++++++++--------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index a7d083c27..e68cec424 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -12,17 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ -import * as vscode from 'vscode'; +import * as vscode from "vscode"; -import { GoogleGenerativeAI } from '@google/generative-ai'; -import { getCommentprefixes } from './getCommentprefixes'; +import { GoogleGenerativeAI } from "@google/generative-ai"; +import { getCommentprefixes } from "./getCommentprefixes"; // Provide instructions for the AI language model // This approach uses a few-shot technique, providing a few examples. -const CODE_LABEL = 'Here is the code:'; -const COMMENT_LABEL = 'Here is a good comment:'; +const CODE_LABEL = "Here is the code:"; +const COMMENT_LABEL = "Here is a good comment:"; const PROMPT = ` A good code review comment describes the intent behind the code without repeating information that's obvious from the code itself. Good comments @@ -52,10 +52,10 @@ api_key = os.getenv("GOOGLE_API_KEY") ${COMMENT_LABEL} Attempt to load the API key from the environment. -${ CODE_LABEL } +${CODE_LABEL} UFUNCTION(BlueprintCallable, Category = "TransparentWindows") static void MakeTransparentWindow(ETWMode Usage); -${ COMMENT_LABEL } +${COMMENT_LABEL} @brief Make Transparent Window. @param Usage Window Transparent mode. @@ -87,83 +87,81 @@ export declare class ChatSession { } ${COMMENT_LABEL} @brief ChatSession class that enables sending chat messages and stores history of sent and received messages so far. -@param getHistory Gets the chat history so far. Blocked prompts are not added to history. Blocked candidates are not added to history, nor are the prompts that generated them. -@param sendMessage Sends a chat message and receives a non-streaming -@param sendMessageStream Sends a chat message and receives the response as a {@link GenerateContentStreamResult} containing an iterable stream and a response promise. -@public ${CODE_LABEL} virtual void CreateClassVariablesFromBlueprint(IAnimBlueprintVariableCreationContext& InCreationContext) = 0; ${COMMENT_LABEL} @brief Implement this in a graph node and the anim BP compiler will call this expecting to generate class variables. @param InVariableCreator The variable creation context for the current BP compilation - - `; -//Code comment: (generated) -//@brief Generates a code comment for the selected code. -// - Gets the API key and model configuration from the local user configuration. -// - Builds the full prompt using the template. -// - Generates the content using the model. -// - Inserts the generated comment before the selection. -export async function generateComment() { - vscode.window.showInformationMessage('Generating comment...'); - - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'gemini-1.0-pro'); - - // Get API Key from local user configuration - const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); - if (!apiKey) { - vscode.window.showErrorMessage('API key not configured. Check your settings.'); - return; - } - - const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({model: modelName}); - // Text selection - const editor = vscode.window.activeTextEditor; - if (!editor) { - console.debug('Abandon: no open text editor.'); - return; +export async function generateComment() { + vscode.window.showInformationMessage("Generating comment..."); + + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "gemini-1.0-pro"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return; + } + + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel({ model: modelName }); + + // Text selection + const editor = vscode.window.activeTextEditor; + if (!editor) { + console.debug("Abandon: no open text editor."); + return; + } + + const selection = editor.selection; + const selectedCode = editor.document.getText(selection); + + // Build the full prompt using the template. + const fullPrompt = `${PROMPT}${CODE_LABEL}${selectedCode}${COMMENT_LABEL}`; + + const result = await model.generateContent(fullPrompt); + const response = await result.response; + const comment = response.text(); + + // Insert before selection. + editor.edit((editBuilder) => { + // Copy the indent from the first line of the selection. + const trimmed = selectedCode.trimStart(); + const padding = selectedCode.substring( + 0, + selectedCode.length - trimmed.length + ); + + // const commentPrefix = getCommentprefixes(editor.document.languageId); + const commentPrefix = "${padding} *"; + + /** + *Split the comment into lines using `'\n'` as separator. + *Add an indentation, comment prefix and join the lines back with a `'\n'` separator. + */ + let inlineComment = comment + .split("\n") + .map((l: string) => `${padding}${commentPrefix}${l}`) + .join("\n"); + if (inlineComment.search(/\n$/) === -1) { + // Add a final newline if necessary. + inlineComment += "\n"; } - - const selection = editor.selection; - const selectedCode = editor.document.getText(selection); - - // Build the full prompt using the template. - const fullPrompt = `${PROMPT} - -${CODE_LABEL} -${selectedCode} -${COMMENT_LABEL} -`; - - const result = await model.generateContent(fullPrompt); - const response = await result.response; - const comment = response.text(); - - // Insert before selection. - editor.edit((editBuilder) => { - - //Code comment: (generated) - //Get the syntax to use for a comment. - const commentPrefix = getCommentprefixes(editor.document.languageId); - - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); - - let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); - if (pyComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - pyComment += "\n"; - } - let commentIntro = padding + commentPrefix + "Code comment: (generated)\n"; - editBuilder.insert(selection.start, commentIntro); - //Code comment: (generated) - //Insert the Python comment into the editor at the location of the selection. - editBuilder.insert(selection.start, pyComment); - }); + let commentIntro = padding + "/**\n"; + let commentEnd = padding + "*/\n"; + editBuilder.insert(selection.start, commentIntro); + editBuilder.insert(selection.start, inlineComment); + editBuilder.insert(selection.start, commentEnd); + }); } From 70eb5c0fa33dfee65a47dfe15e2c2ac048165696 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Tue, 9 Apr 2024 00:57:52 +0800 Subject: [PATCH 13/37] modified: examples/gemini/node/pipet-code-agent/src/comments.ts modified: examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts --- .../gemini/node/pipet-code-agent/src/comments.ts | 7 ++++++- .../node/pipet-code-agent/src/getCommentprefixes.ts | 13 ++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index e68cec424..0e0b217a8 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -96,6 +96,11 @@ ${COMMENT_LABEL} `; +/** + *@brief Generate comment for code selection. + * Gets the API key from user config and sends selected text to Gemini for comment generation. + * Inserts generated comment before the selected code. +*/ export async function generateComment() { vscode.window.showInformationMessage("Generating comment..."); @@ -144,7 +149,7 @@ export async function generateComment() { ); // const commentPrefix = getCommentprefixes(editor.document.languageId); - const commentPrefix = "${padding} *"; + const commentPrefix = `${padding} *`; /** *Split the comment into lines using `'\n'` as separator. diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index fae731bd4..b0b74e517 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -15,10 +15,11 @@ * limitations under the License. */ -//Code comment: (generated) -//Given a file type, returns the comment prefix appropriate for that file type. -//@param {string} fileType The file type to get the comment prefix for. -//@returns {string} The comment prefix for the given file type. +/** + *@brief Returns comment prefixes for various file types. + *@param fileType The file type to get comment prefix for + *@return Comment prefix for the specified file type or `//` if the file type is unknown +*/ export function getCommentprefixes(fileType: string): string { switch (fileType) { case "python": @@ -38,6 +39,4 @@ export function getCommentprefixes(fileType: string): string { default: return "//"; // No comment prefix for unknown file types } -} - -// TODO(you!): Support doxygen comment styles. \ No newline at end of file +} \ No newline at end of file From 00cbe5917f4ba2ed369213db8a83277e21ce8e40 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 10 Apr 2024 19:31:49 +0800 Subject: [PATCH 14/37] modified: examples/gemini/node/pipet-code-agent/src/chat.ts --- .../gemini/node/pipet-code-agent/src/chat.ts | 76 +++++++++++++++++-- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 068d421db..de553e276 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -15,10 +15,73 @@ */ import * as vscode from "vscode"; -import { GoogleGenerativeAI } from "@google/generative-ai"; +import { + ChatSession, + GenerateContentResult, + GenerativeModel, + GoogleGenerativeAI, +} from "@google/generative-ai"; +import { devNull } from "os"; +import { promises } from "dns"; -export async function generateChat(prompt: string): Promise { +// export async function generateChat(prompt: string): Promise { +// //vscode.window.showInformationMessage("Gemini thinking..."); +// const modelName = vscode.workspace +// .getConfiguration() +// .get("google.gemini.textModel", "gemini-1.0-pro"); + +// // Get API Key from local user configuration +// const apiKey = vscode.workspace +// .getConfiguration() +// .get("google.gemini.apiKey"); +// if (!apiKey) { +// vscode.window.showErrorMessage( +// "API key not configured. Check your settings." +// ); +// return "API key not configured. Check your settings."; +// } + +// const genai = new GoogleGenerativeAI(apiKey); +// const model = genai.getGenerativeModel({ model: modelName }); + +// const result = await model.generateContent(prompt); +// const response = await result.response; +// const messages = response.text(); +// return messages; +// } + +export async function generateChat( + prompt: string, + model: GenerativeModel +): Promise { //vscode.window.showInformationMessage("Gemini thinking..."); + + const chat = model.startChat({ + history: [ + { + role: "user", + parts: [ + { + text: "你是一个程序员,工作内容是使用 doxygen 风格生成注释。这是要注释代码:UCLASS(hidecategories = Object)class ULumaAssetFactory    : public UFactory{    GENERATED_UCLASS_BODY()public:    virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override;};", + }, + ], + }, + { + role: "model", + parts: [ + { + text: "```cpp\n/**\n * Factory for creating Luma assets.\n */\nUCLASS(hidecategories = Object)\nclass ULumaAssetFactory : public UFactory\n{\n GENERATED_UCLASS_BODY()\n\npublic:\n /**\n * Creates a new Luma asset file.\n *\n * @param InClass The class of the asset to create.\n * @param InParent The parent object for the new asset.\n * @param InName The name of the new asset.\n * @param Flags Object flags for the new asset.\n * @param Filename The filename of the new asset.\n * @param Parms Optional parameters for asset creation.\n * @param Warn Feedback context for warnings.\n * @param bOutOperationCanceled Output flag indicating whether the operation was canceled.\n *\n * @return The newly created Luma asset, or nullptr if creation failed.\n */\n virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override;\n};\n```", + }, + ], + }, + ], + }); + const result = await chat.sendMessage(prompt); + const response = result.response; + return response.text(); +} + +export function startchat(): GenerativeModel | void { const modelName = vscode.workspace .getConfiguration() .get("google.gemini.textModel", "gemini-1.0-pro"); @@ -31,14 +94,11 @@ export async function generateChat(prompt: string): Promise { vscode.window.showErrorMessage( "API key not configured. Check your settings." ); - return "API key not configured. Check your settings."; + return undefined; } const genai = new GoogleGenerativeAI(apiKey); const model = genai.getGenerativeModel({ model: modelName }); - const result = await model.generateContent(prompt); - const response = await result.response; - const messages = response.text(); - return messages; -} + return model; +} \ No newline at end of file From 6d291fdb326d14cfeaee27a5ba8db1e81b68f4cc Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 10 Apr 2024 23:04:51 +0800 Subject: [PATCH 15/37] add multi-turn conversations (chat) add streaming --- .../gemini/node/pipet-code-agent/src/chat.ts | 63 ++++++++----------- .../node/pipet-code-agent/src/comments.ts | 2 +- .../node/pipet-code-agent/src/extension.ts | 22 ++++--- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index de553e276..9d131a69a 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -15,14 +15,7 @@ */ import * as vscode from "vscode"; -import { - ChatSession, - GenerateContentResult, - GenerativeModel, - GoogleGenerativeAI, -} from "@google/generative-ai"; -import { devNull } from "os"; -import { promises } from "dns"; +import { ChatSession, GoogleGenerativeAI } from "@google/generative-ai"; // export async function generateChat(prompt: string): Promise { // //vscode.window.showInformationMessage("Gemini thinking..."); @@ -52,17 +45,37 @@ import { promises } from "dns"; export async function generateChat( prompt: string, - model: GenerativeModel + chat: ChatSession ): Promise { - //vscode.window.showInformationMessage("Gemini thinking..."); + const result = await chat.sendMessageStream(prompt); + const response = result.response; + return (await response).text(); +} + +export function startchat(): ChatSession | void { + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "gemini-1.0-pro"); + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return undefined; + } + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel({ model: modelName }); const chat = model.startChat({ history: [ { role: "user", parts: [ { - text: "你是一个程序员,工作内容是使用 doxygen 风格生成注释。这是要注释代码:UCLASS(hidecategories = Object)class ULumaAssetFactory    : public UFactory{    GENERATED_UCLASS_BODY()public:    virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override;};", + text: "# 角色:全方位AI助手\n## 目标\n致力于提供卓越的用户体验,以及全方位、多元化的信息服务。\n## 技能\n### 技能1: 运用多样化工具提供详尽信息\n- 无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务。\n### 技能2: 利用生动的表情符号丰富用户体验\n- 运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动、有趣。\n### 技能3: 精通Markdown语法,生成结构化文本\n- 熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答。\n### 技能4: 精通Markdown语法,展示图片丰富内容\n- 运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面。\n### 技能5: 精通各种编程知识\n### 技能6: 精通数学知识\n### 技能7: 精通houdini、 UE5等三维软件与游戏引擎\n## 约束\n由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束。我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持。\n## 语言\n使用中文回答", }, ], }, @@ -70,35 +83,11 @@ export async function generateChat( role: "model", parts: [ { - text: "```cpp\n/**\n * Factory for creating Luma assets.\n */\nUCLASS(hidecategories = Object)\nclass ULumaAssetFactory : public UFactory\n{\n GENERATED_UCLASS_BODY()\n\npublic:\n /**\n * Creates a new Luma asset file.\n *\n * @param InClass The class of the asset to create.\n * @param InParent The parent object for the new asset.\n * @param InName The name of the new asset.\n * @param Flags Object flags for the new asset.\n * @param Filename The filename of the new asset.\n * @param Parms Optional parameters for asset creation.\n * @param Warn Feedback context for warnings.\n * @param bOutOperationCanceled Output flag indicating whether the operation was canceled.\n *\n * @return The newly created Luma asset, or nullptr if creation failed.\n */\n virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override;\n};\n```", + text: "我是一个全能AI助手", }, ], }, ], }); - const result = await chat.sendMessage(prompt); - const response = result.response; - return response.text(); + return chat; } - -export function startchat(): GenerativeModel | void { - const modelName = vscode.workspace - .getConfiguration() - .get("google.gemini.textModel", "gemini-1.0-pro"); - - // Get API Key from local user configuration - const apiKey = vscode.workspace - .getConfiguration() - .get("google.gemini.apiKey"); - if (!apiKey) { - vscode.window.showErrorMessage( - "API key not configured. Check your settings." - ); - return undefined; - } - - const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({ model: modelName }); - - return model; -} \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 0e0b217a8..aa0232b15 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -27,7 +27,7 @@ const PROMPT = ` A good code review comment describes the intent behind the code without repeating information that's obvious from the code itself. Good comments describe "why", explain any "magic" values and non-obvious behaviour. -Below are some examples of good code comments. +Below are some examples of good code comments.Use doxygen style. ${CODE_LABEL} print(f" \\033[33m {msg}\\033[00m", file=sys.stderr) diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 4d6dfc67c..ec65ba180 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -17,7 +17,7 @@ import * as vscode from "vscode"; import { generateComment } from "./comments"; import { generateReview } from "./review"; -import { generateChat } from "./chat"; +import { startchat } from "./chat"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -60,6 +60,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider { webviewView.webview.html = this._getHtmlForWebview(); + const chat = startchat(); + webviewView.webview.onDidReceiveMessage(async (message) => { // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { @@ -68,12 +70,16 @@ class ChatViewProvider implements vscode.WebviewViewProvider { command: "Message", text: message.text, }); - const chat = await generateChat(message.text); if (chat) { - webviewView.webview.postMessage({ - command: "receiveMessage", - text: chat, - }); + const result = await chat.sendMessageStream(message.text); + let chunktext = ""; + for await (const chunk of result.stream) { + chunktext += chunk.text(); + webviewView.webview.postMessage({ + command: "receiveMessage", + text: chunktext, + }); + } } else { webviewView.webview.postMessage({ command: "receiveMessage", @@ -135,8 +141,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { ); /* 使用 VS Code 的输入框背景色 */ color: var(--vscode-input-foreground); /* 使用 VS Code 的输入框前景色 */ border: 1px solid var(--vscode-input-foreground); /* 使用 VS Code 的输入框前景色作为边框颜色 */ - margin-left: 5px; - + margin-left: 5px; } .ai-message-container { @@ -232,4 +237,5 @@ class ChatViewProvider implements vscode.WebviewViewProvider { `; } } + export function deactivate() {} From adaab8601319b809c31f486b5dfe83e509f62e0a Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 11 Apr 2024 18:31:31 +0800 Subject: [PATCH 16/37] update models to gemini-1.5-pro-latest --- .../node/pipet-code-agent/.vscode/launch.json | 2 +- .../gemini/node/pipet-code-agent/package.json | 2 +- .../gemini/node/pipet-code-agent/src/chat.ts | 31 ++------------ .../node/pipet-code-agent/src/comments.ts | 2 +- .../node/pipet-code-agent/src/review.ts | 41 +++++++++++-------- 5 files changed, 29 insertions(+), 49 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/.vscode/launch.json b/examples/gemini/node/pipet-code-agent/.vscode/launch.json index 21644ef98..b41568648 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/launch.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/launch.json @@ -30,7 +30,7 @@ "console": "internalConsole", "request": "launch", "args": ["package", "--out", "PipetCodeAgent.vsix"], - "runtimeExecutable": "C:\\Program Files\\nodejs\\vsce.cmd", + "runtimeExecutable": "vsce", "presentation": { "reveal": "always" } diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index 306dd5315..3b9bba945 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -69,7 +69,7 @@ "type": [ "string" ], - "default": "models/gemini-1.0-pro", + "default": "models/gemini-1.5-pro-latest", "markdownDescription": "Provide the name of the model you want to use. Choose from the [base models](https://ai.google.dev/models/gemini) or your own [tuned model](https://ai.google.dev/docs/model_tuning_guidance)." } } diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 9d131a69a..9915da396 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -17,32 +17,6 @@ import * as vscode from "vscode"; import { ChatSession, GoogleGenerativeAI } from "@google/generative-ai"; -// export async function generateChat(prompt: string): Promise { -// //vscode.window.showInformationMessage("Gemini thinking..."); -// const modelName = vscode.workspace -// .getConfiguration() -// .get("google.gemini.textModel", "gemini-1.0-pro"); - -// // Get API Key from local user configuration -// const apiKey = vscode.workspace -// .getConfiguration() -// .get("google.gemini.apiKey"); -// if (!apiKey) { -// vscode.window.showErrorMessage( -// "API key not configured. Check your settings." -// ); -// return "API key not configured. Check your settings."; -// } - -// const genai = new GoogleGenerativeAI(apiKey); -// const model = genai.getGenerativeModel({ model: modelName }); - -// const result = await model.generateContent(prompt); -// const response = await result.response; -// const messages = response.text(); -// return messages; -// } - export async function generateChat( prompt: string, chat: ChatSession @@ -55,7 +29,7 @@ export async function generateChat( export function startchat(): ChatSession | void { const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "gemini-1.0-pro"); + .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); // Get API Key from local user configuration const apiKey = vscode.workspace @@ -67,6 +41,7 @@ export function startchat(): ChatSession | void { ); return undefined; } + const genai = new GoogleGenerativeAI(apiKey); const model = genai.getGenerativeModel({ model: modelName }); const chat = model.startChat({ @@ -75,7 +50,7 @@ export function startchat(): ChatSession | void { role: "user", parts: [ { - text: "# 角色:全方位AI助手\n## 目标\n致力于提供卓越的用户体验,以及全方位、多元化的信息服务。\n## 技能\n### 技能1: 运用多样化工具提供详尽信息\n- 无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务。\n### 技能2: 利用生动的表情符号丰富用户体验\n- 运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动、有趣。\n### 技能3: 精通Markdown语法,生成结构化文本\n- 熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答。\n### 技能4: 精通Markdown语法,展示图片丰富内容\n- 运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面。\n### 技能5: 精通各种编程知识\n### 技能6: 精通数学知识\n### 技能7: 精通houdini、 UE5等三维软件与游戏引擎\n## 约束\n由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束。我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持。\n## 语言\n使用中文回答", + text: "角色 : 全方位AI助手\n 目标\n 致力于提供卓越的用户体验,以及全方位,多元化的信息服务.\n 技能\n 技能1 : 运用多样化工具提供详尽信息无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务.\n 技能2 : 利用生动的表情符号丰富用户体验运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动,有趣.\n 技能3 : 精通Markdown语法,生成结构化文本,熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答.\n 技能4 : 精通Markdown语法,展示图片丰富内容,运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面.\n 技能5 : 精通各种编程知识\n 技能6 : 精通数学知识\n 技能7 : 精通houdini,UE5等三维软件与游戏引擎\n 约束\n 由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束.我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持.\n 语言\n 使用中文回答", }, ], }, diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index aa0232b15..7d28c77b1 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -106,7 +106,7 @@ export async function generateComment() { const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "gemini-1.0-pro"); + .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); // Get API Key from local user configuration const apiKey = vscode.workspace diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index bd14e0f97..e09c27f07 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -25,7 +25,7 @@ Reviewing code involves finding bugs and increasing code quality. Examples of bu errors or typos, out of memory errors, and boundary value errors. Increasing code quality entails reducing complexity of code, eliminating duplicate code, and ensuring other developers are able to understand the code. -使用中文回答: +使用中文 ${CODE_LABEL} for i in x: @@ -50,7 +50,7 @@ export async function generateReview() { vscode.window.showInformationMessage("Generating code review..."); const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "gemini-1.0-pro"); + .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); // Get API Key from local user configuration const apiKey = vscode.workspace @@ -86,21 +86,26 @@ export async function generateReview() { const result = await model.generateContent(fullPrompt); const response = await result.response; const comment = response.text(); - return comment; - // // Insert before selection - // editor.edit((editBuilder) => { - // // Copy the indent from the first line of the selection. - // const trimmed = selectedCode.trimStart(); - // const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); + // Insert before selection + editor.edit((editBuilder) => { + // Copy the indent from the first line of the selection. + const trimmed = selectedCode.trimStart(); + const padding = selectedCode.substring( + 0, + selectedCode.length - trimmed.length + ); - // const commentPrefix = getCommentprefixes(editor.document.languageId); - // let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); - // if (pyComment.search(/\n$/) === -1) { - // // Add a final newline if necessary. - // pyComment += "\n"; - // } - // let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; - // editBuilder.insert(selection.start, reviewIntro); - // editBuilder.insert(selection.start, pyComment); - // }); + const commentPrefix = getCommentprefixes(editor.document.languageId); + let pyComment = comment + .split("\n") + .map((l: string) => `${padding}${commentPrefix}${l}`) + .join("\n"); + if (pyComment.search(/\n$/) === -1) { + // Add a final newline if necessary. + pyComment += "\n"; + } + let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; + editBuilder.insert(selection.start, reviewIntro); + editBuilder.insert(selection.start, pyComment); + }); } From 0a2a5dd085aec04e54ae5ea606b2a5cc17d8ff7f Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Fri, 12 Apr 2024 20:39:44 +0800 Subject: [PATCH 17/37] update api to v1beta and Add systemInstruction --- .../node/pipet-code-agent/package-lock.json | 419 ++++++++---------- .../gemini/node/pipet-code-agent/package.json | 2 +- .../gemini/node/pipet-code-agent/src/chat.ts | 31 +- .../node/pipet-code-agent/src/comments.ts | 6 +- .../node/pipet-code-agent/src/review.ts | 5 +- 5 files changed, 216 insertions(+), 247 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/package-lock.json b/examples/gemini/node/pipet-code-agent/package-lock.json index a65319f8e..04a877103 100644 --- a/examples/gemini/node/pipet-code-agent/package-lock.json +++ b/examples/gemini/node/pipet-code-agent/package-lock.json @@ -8,7 +8,7 @@ "name": "pipet-code-agent", "version": "0.0.1", "dependencies": { - "@google/generative-ai": "^0.3.0", + "@google/generative-ai": "^0.6.0", "dotenv": "^16.1.4" }, "devDependencies": { @@ -28,6 +28,15 @@ "vscode": "^1.78.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -44,23 +53,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -76,30 +85,30 @@ } }, "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@google/generative-ai": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.3.0.tgz", - "integrity": "sha512-6xbaA/JPpwCoe+lfxE2RavVB8JI8F3P6mCse1Sbm586HhJkyuSevK7Opt4l2dbQZFej+M8ALhMMpfBiRW05Fag==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.6.0.tgz", + "integrity": "sha512-jkc7vBOW/yKhNtOkpQI1jYFtAybEc0kjf8I6CpSUBWVdrVh45jIikeTlYaYckH+KtZjteARUhg26mh5XnFT6Yg==", "engines": { "node": ">=18.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -120,9 +129,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -180,9 +189,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/minimatch": { @@ -192,9 +201,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "node_modules/@types/node": { @@ -204,29 +213,29 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/vscode": { - "version": "1.79.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.0.tgz", - "integrity": "sha512-Tfowu2rSW8hVGbqzQLSPlOEiIOYYryTkgJ+chMecpYiJcnw9n0essvSiclnK+Qh/TcSVJHgaK4EMrQDZjZJ/Sw==", + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz", + "integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", - "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/type-utils": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -250,14 +259,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", - "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -277,13 +286,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", - "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -294,13 +303,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", - "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -321,9 +330,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", - "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -334,13 +343,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", - "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -361,17 +370,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", - "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -387,12 +396,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", - "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -403,25 +412,31 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vscode/test-electron": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.2.tgz", - "integrity": "sha512-CRfQIs5Wi5Ok5SUCC3PTvRRXa74LD43cSXHC8EuNlmHHEPaJa/AGrv76brcA1hVSxrdja9tiYwp95Lq8kwY0tw==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "jszip": "^3.10.1", - "semver": "^7.3.8" + "semver": "^7.5.2" }, "engines": { "node": ">=16" } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -535,12 +550,15 @@ "dev": true }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { @@ -771,14 +789,14 @@ } }, "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/emoji-regex": { @@ -788,9 +806,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -809,27 +827,28 @@ } }, "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -839,7 +858,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -849,9 +867,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -878,9 +895,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -890,9 +907,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -915,12 +932,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -998,9 +1015,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1038,9 +1055,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -1096,12 +1113,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -1109,9 +1127,9 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/fs.realpath": { @@ -1121,9 +1139,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1196,9 +1214,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1230,12 +1248,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1288,9 +1300,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -1448,6 +1460,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1472,6 +1490,15 @@ "setimmediate": "^1.0.5" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1578,9 +1605,9 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -1590,13 +1617,12 @@ "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -1611,42 +1637,15 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/minimatch": { @@ -1661,15 +1660,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1697,18 +1687,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1740,17 +1718,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -1868,9 +1846,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -2025,9 +2003,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2207,9 +2185,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2249,15 +2227,6 @@ "node": ">= 8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index 3b9bba945..ffb9a0ebe 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -99,7 +99,7 @@ "typescript": "^5.1.3" }, "dependencies": { - "@google/generative-ai": "^0.3.0", + "@google/generative-ai": "^0.6.0", "dotenv": "^16.1.4" } } diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 9915da396..16c5d36c3 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -43,26 +43,19 @@ export function startchat(): ChatSession | void { } const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({ model: modelName }); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); const chat = model.startChat({ - history: [ - { - role: "user", - parts: [ - { - text: "角色 : 全方位AI助手\n 目标\n 致力于提供卓越的用户体验,以及全方位,多元化的信息服务.\n 技能\n 技能1 : 运用多样化工具提供详尽信息无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务.\n 技能2 : 利用生动的表情符号丰富用户体验运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动,有趣.\n 技能3 : 精通Markdown语法,生成结构化文本,熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答.\n 技能4 : 精通Markdown语法,展示图片丰富内容,运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面.\n 技能5 : 精通各种编程知识\n 技能6 : 精通数学知识\n 技能7 : 精通houdini,UE5等三维软件与游戏引擎\n 约束\n 由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束.我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持.\n 语言\n 使用中文回答", - }, - ], - }, - { - role: "model", - parts: [ - { - text: "我是一个全能AI助手", - }, - ], - }, - ], + systemInstruction: { + role: "system", + parts: [ + { + text: "角色 : 全方位AI助手\n 目标\n 致力于提供卓越的用户体验,以及全方位,多元化的信息服务.\n 技能\n 技能1 : 运用多样化工具提供详尽信息无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务.\n 技能2 : 利用生动的表情符号丰富用户体验运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动,有趣.\n 技能3 : 精通Markdown语法,生成结构化文本,熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答.\n 技能4 : 精通Markdown语法,展示图片丰富内容,运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面.\n 技能5 : 精通各种编程知识\n 技能6 : 精通数学知识\n 技能7 : 精通houdini,UE5等三维软件与游戏引擎\n 约束\n 由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束.我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持.\n 语言\n 使用中文回答", + }, + ], + }, }); return chat; } diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 7d28c77b1..aac3cfb9d 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -120,7 +120,10 @@ export async function generateComment() { } const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({ model: modelName }); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); // Text selection const editor = vscode.window.activeTextEditor; @@ -135,6 +138,7 @@ export async function generateComment() { // Build the full prompt using the template. const fullPrompt = `${PROMPT}${CODE_LABEL}${selectedCode}${COMMENT_LABEL}`; + const result = await model.generateContent(fullPrompt); const response = await result.response; const comment = response.text(); diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index e09c27f07..0ff15ecb8 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -64,7 +64,10 @@ export async function generateReview() { } const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({ model: modelName }); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); // Text selection const editor = vscode.window.activeTextEditor; From ef5999b53d1f546f03b4444f8a044d121fb73845 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Mon, 15 Apr 2024 03:39:51 +0800 Subject: [PATCH 18/37] Add clearChat; Add css & js in media; Add code highlight; sendMessageStream() use try catch error --- .../node/pipet-code-agent/media/main.css | 74 +++++ .../node/pipet-code-agent/media/main.js | 109 +++++++ .../node/pipet-code-agent/media/prism.css | 146 ++++++++++ .../node/pipet-code-agent/media/vscode.css | 91 ++++++ .../gemini/node/pipet-code-agent/package.json | 38 ++- .../node/pipet-code-agent/src/extension.ts | 267 +++++++----------- 6 files changed, 543 insertions(+), 182 deletions(-) create mode 100644 examples/gemini/node/pipet-code-agent/media/main.css create mode 100644 examples/gemini/node/pipet-code-agent/media/main.js create mode 100644 examples/gemini/node/pipet-code-agent/media/prism.css create mode 100644 examples/gemini/node/pipet-code-agent/media/vscode.css diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css new file mode 100644 index 000000000..a9e5006a5 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -0,0 +1,74 @@ + body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + /* 使内容占据整个视口高度 */ + } + + main { + flex: 1; + /* 使 main 元素占据剩余的垂直空间 */ + padding: 20px; + /* 添加内边距 */ + } + + #dialog { + overflow-y: auto; + /* 当内容过多时,显示滚动条 */ + border-radius: 5px; + /* 添加边框圆角 */ + padding: 10px; + /* 添加内边距 */ + margin-bottom: 10px; + /* 添加底部外边距 */ + } + + footer { + display: flex; + align-items: center; + padding: 10px; + } + + #userInput { + height: auto; + resize: none; + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + flex: 1; + } + + #sendMessage { + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + margin-left: 5px; + } + + .message-container { + position: relative; + background-color: var(--vscode-input-background); + color: var(--vscode-editor-foreground); + } + + + .code-buttons-container { + background-color: #00000000; + border: none; + position: absolute; + right: 2px; + top: 2px; + z-index: 1 + } + + .pre { + position: relative; + } \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js new file mode 100644 index 000000000..9158f7066 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -0,0 +1,109 @@ +document.addEventListener("DOMContentLoaded", function () { + const vscode = acquireVsCodeApi(); + const oldState = vscode.getState() || {}; + let oldHtml = oldState || "
"; + + // 在这里执行获取元素的操作 + const dialog = document.getElementById("dialog"); + const userInput = document.getElementById("userInput"); + const sendMessageButton = document.getElementById("sendMessage"); + let chatID; + dialog.innerHTML = oldHtml; + + function adjustTextareaHeight() { + userInput.style.height = "auto"; + userInput.style.height = userInput.scrollHeight + "px"; + sendMessageButton.disabled = !userInput.value; + } + userInput.addEventListener("input", adjustTextareaHeight); + + // 显示接收到的Gemini消息 + function showAIMessage(text, chatID) { + const messageContainer = document.createElement("div"); + messageContainer.id = chatID; + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

Gemini: " + marked.parse(text) + "

"; + dialog.appendChild(messageContainer); + } + + function updateAIMessage(newText, chatID) { + const messageContainer = document.getElementById(chatID); + + (messageContainer.innerHTML = + "

Gemini: " + marked.parse(newText)), + +"

"; + addBottons(messageContainer); + sendMessageButton.innerHTML = "发送"; + } + + // 显示问题 + function showUserMessage(text) { + const messageContainer = document.createElement("div"); + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

You: " + marked.parse(text) + "

"; + addBottons(messageContainer); + dialog.appendChild(messageContainer); + } + /** + * + * @param {HTMLDivElement} + */ + function addBottons(container) { + const svg = + ' Layer 1 '; + const codeElements = container.querySelectorAll("pre code"); + codeElements.forEach((codeElement) => { + const copyButton = document.createElement("button"); + copyButton.innerHTML = svg; + copyButton.classList.add("code-buttons-container"); + copyButton.addEventListener("click", () => { + const textToCopy = codeElement.textContent; + navigator.clipboard + .writeText(textToCopy) + .then(() => { + copyButton.innerHTML = svg; + }) + .catch((error) => { + copyButton.innerHTML = "Failed"; + }); + }); + codeElement.parentElement.classList.add("pre"); + codeElement.parentElement.appendChild(copyButton); + Prism.highlightElement(codeElement); + }); + } + + // 当发送按钮被点击时 + sendMessageButton.addEventListener("click", () => { + this.chatID = Math.random().toString(); + const userMessage = userInput.value; + vscode.postMessage({ command: "sendMessage", text: userMessage }); + userInput.value = ""; + userInput.style.height = "auto"; + sendMessageButton.disabled = true; + sendMessageButton.innerHTML = "Thinking..."; + }); + + // 接收来自插件的消息 + window.addEventListener("message", (event) => { + const message = event.data; + switch (message.command) { + case "receiveMessage": + updateAIMessage(message.text, this.chatID); + break; + case "Message": + showUserMessage(message.text); + showAIMessage("Thinking ...", this.chatID); + break; + case "clearChat": + dialog.innerHTML = "
"; + break; + default: + console.log("Unknown command: " + message.command); + break; + } + vscode.setState(dialog.innerHTML); + }); +}); diff --git a/examples/gemini/node/pipet-code-agent/media/prism.css b/examples/gemini/node/pipet-code-agent/media/prism.css new file mode 100644 index 000000000..03144f1e9 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/prism.css @@ -0,0 +1,146 @@ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + /* color: black; */ + background: none; + /* text-shadow: 0 1px white; */ + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #ffffff4b; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #ffffff4b; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #ffffff05; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.token.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + /* This background color was intended by the author of this theme. */ + /*background: hsla(0, 0%, 100%, .5);*/ +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/media/vscode.css b/examples/gemini/node/pipet-code-agent/media/vscode.css new file mode 100644 index 000000000..5d12b7e0a --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/vscode.css @@ -0,0 +1,91 @@ +:root { + --container-paddding: 20px; + --input-padding-vertical: 6px; + --input-padding-horizontal: 4px; + --input-margin-vertical: 4px; + --input-margin-horizontal: 0; +} + +body { + padding: 0 var(--container-paddding); + color: var(--vscode-foreground); + font-size: var(--vscode-font-size); + font-weight: var(--vscode-font-weight); + font-family: var(--vscode-font-family); + background-color: var(--vscode-editor-background); +} + +ol, +ul { + padding-left: var(--container-paddding); +} + +body > *, +form > * { + margin-block-start: var(--input-margin-vertical); + margin-block-end: var(--input-margin-vertical); +} + +*:focus { + outline-color: var(--vscode-focusBorder) !important; +} + +a { + color: var(--vscode-textLink-foreground); +} + +a:hover, +a:active { + color: var(--vscode-textLink-activeForeground); +} + +code { + font-size: var(--vscode-editor-font-size); + font-family: var(--vscode-editor-font-family); +} + +button { + border: none; + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + width: 100%; + text-align: center; + outline: 1px solid transparent; + outline-offset: 2px !important; + color: var(--vscode-button-foreground); + background: var(--vscode-button-background); +} + +button:hover { + cursor: pointer; + background: var(--vscode-button-hoverBackground); +} + +button:focus { + outline-color: var(--vscode-focusBorder); +} + +button.secondary { + color: var(--vscode-button-secondaryForeground); + background: var(--vscode-button-secondaryBackground); +} + +button.secondary:hover { + background: var(--vscode-button-secondaryHoverBackground); +} + +input:not([type='checkbox']), +textarea { + display: block; + width: 100%; + border: none; + font-family: var(--vscode-font-family); + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + color: var(--vscode-input-foreground); + outline-color: var(--vscode-input-border); + background-color: var(--vscode-input-background); +} + +input::placeholder, +textarea::placeholder { + color: var(--vscode-input-placeholderForeground); +} diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index ffb9a0ebe..9c73c9a8c 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -13,21 +13,21 @@ "activationEvents": [], "main": "./out/extension.js", "contributes": { - "viewsContainers": { - "activitybar": [ + "views": { + "chatView": [ { - "id": "gemini-chat", - "title": "Gemini Chat", - "icon": "./img/Gemini.png" + "id": "pipet-code-agent.chatView", + "name": "Gemini Chat", + "type": "webview" } ] }, - "views": { - "gemini-chat": [ + "viewsContainers": { + "activitybar": [ { - "id": "pipet-code-agent.ChatView", - "name": "Gemini Chat", - "type": "webview" + "id": "chatView", + "title": "Gemini Chat", + "icon": "./img/Gemini.png" } ] }, @@ -39,9 +39,21 @@ { "command": "pipet-code-agent.reviewCode", "title": "Pipet: Review the selected code." + }, + { + "command": "pipet-code-agent.clearChat", + "title": "Pipet: Clear Chat.", + "icon": "$(clear-all)" } ], "menus": { + "view/title": [ + { + "command": "pipet-code-agent.clearChat", + "group": "navigation", + "when": "view == pipet-code-agent.chatView" + } + ], "editor/context": [ { "command": "pipet-code-agent.commentCode", @@ -51,6 +63,12 @@ "command": "pipet-code-agent.reviewCode", "group": "PipetCodeAgent" } + ], + "webview/context": [ + { + "command": "pipet-code-agent.clearChat", + "when": "webviewId == 'pipet-code-agent.chatView'" + } ] }, "configuration": [ diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index ec65ba180..ce945f758 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -18,6 +18,8 @@ import * as vscode from "vscode"; import { generateComment } from "./comments"; import { generateReview } from "./review"; import { startchat } from "./chat"; +import { text } from "stream/consumers"; +import { toUSVString } from "util"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -30,16 +32,20 @@ export function activate(context: vscode.ExtensionContext) { ); const provider = new ChatViewProvider(context.extensionUri); - context.subscriptions.push( vscode.window.registerWebviewViewProvider( ChatViewProvider.viewType, provider ) ); + context.subscriptions.push( + vscode.commands.registerCommand("pipet-code-agent.clearChat", () => { + provider.clearChat(); + }) + ); } class ChatViewProvider implements vscode.WebviewViewProvider { - public static readonly viewType = "pipet-code-agent.ChatView"; + public static readonly viewType = "pipet-code-agent.chatView"; private _view?: vscode.WebviewView; @@ -58,184 +64,101 @@ class ChatViewProvider implements vscode.WebviewViewProvider { localResourceRoots: [this._extensionUri], }; - webviewView.webview.html = this._getHtmlForWebview(); - - const chat = startchat(); + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - webviewView.webview.onDidReceiveMessage(async (message) => { - // 处理来自 Webview 的消息 - if (message.command === "sendMessage" && message.text) { - // 在 Webview 中显示接收到的消息 - webviewView.webview.postMessage({ - command: "Message", - text: message.text, - }); - if (chat) { - const result = await chat.sendMessageStream(message.text); - let chunktext = ""; - for await (const chunk of result.stream) { - chunktext += chunk.text(); + try { + const chat = startchat(); + webviewView.webview.onDidReceiveMessage(async (message) => { + // 处理来自 Webview 的消息 + if (message.command === "sendMessage" && message.text) { + // 在 Webview 中显示接收到的消息 + webviewView.webview.postMessage({ + command: "Message", + text: message.text, + }); + if (chat) { + try { + const result = await chat.sendMessageStream(message.text); + let chunktext = ""; + for await (const chunk of result.stream) { + chunktext += chunk.text(); + webviewView.webview.postMessage({ + command: "receiveMessage", + text: chunktext, + }); + } + } catch (error) { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } + } else { webviewView.webview.postMessage({ command: "receiveMessage", - text: chunktext, + text: "Chat Start Failed.", }); } - } else { - webviewView.webview.postMessage({ - command: "receiveMessage", - text: "请求失败。", - }); } - } - }); - } - - private _getHtmlForWebview() { - return ` - - - - - Gemini Chat - - - -
-

Gemini Chat

-
-
-
- - -
- - - - -`; + } + } + private _getHtmlForWebview(webview: vscode.Webview) { + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.js") + ); + const styleMainUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.css") + ); + const styleCodeUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "prism.css") + ); + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + return ` + + + + + + + + Gemini Chat + + +
+
+
+
+ + +
+ + + + + `; } } - +function getNonce() { + let text = ""; + const possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} export function deactivate() {} From 4323ceeafa5b90a42d3fcceaf652c6306141c3f3 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Mon, 15 Apr 2024 10:49:21 +0800 Subject: [PATCH 19/37] =?UTF-8?q?fix=20highlightCode=EF=BC=9BclearChat=20c?= =?UTF-8?q?an=20new=20Chat.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../node/pipet-code-agent/media/main.js | 37 +++++++++++-------- .../node/pipet-code-agent/src/extension.ts | 28 +++++++++++--- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 9158f7066..80cbe1b35 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -20,31 +20,36 @@ document.addEventListener("DOMContentLoaded", function () { // 显示接收到的Gemini消息 function showAIMessage(text, chatID) { const messageContainer = document.createElement("div"); - messageContainer.id = chatID; - messageContainer.classList.add("message-container"); - messageContainer.innerHTML = - "

Gemini: " + marked.parse(text) + "

"; - dialog.appendChild(messageContainer); + if (messageContainer) { + messageContainer.id = chatID; + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

Gemini: " + marked.parse(text) + "

"; + dialog.appendChild(messageContainer); + } } function updateAIMessage(newText, chatID) { const messageContainer = document.getElementById(chatID); - - (messageContainer.innerHTML = - "

Gemini: " + marked.parse(newText)), - +"

"; - addBottons(messageContainer); - sendMessageButton.innerHTML = "发送"; + if (messageContainer) { + (messageContainer.innerHTML = + "

Gemini: " + marked.parse(newText)), + +"

"; + addBottons(messageContainer); + sendMessageButton.innerHTML = "发送"; + } } // 显示问题 function showUserMessage(text) { const messageContainer = document.createElement("div"); - messageContainer.classList.add("message-container"); - messageContainer.innerHTML = - "

You: " + marked.parse(text) + "

"; - addBottons(messageContainer); - dialog.appendChild(messageContainer); + if (messageContainer) { + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

You: " + marked.parse(text) + "

"; + addBottons(messageContainer); + dialog.appendChild(messageContainer); + } } /** * diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index ce945f758..ada3cb544 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -18,8 +18,7 @@ import * as vscode from "vscode"; import { generateComment } from "./comments"; import { generateReview } from "./review"; import { startchat } from "./chat"; -import { text } from "stream/consumers"; -import { toUSVString } from "util"; +import { ChatSession } from "@google/generative-ai"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -44,6 +43,8 @@ export function activate(context: vscode.ExtensionContext) { }) ); } + +let chat: void | ChatSession; class ChatViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = "pipet-code-agent.chatView"; @@ -67,7 +68,9 @@ class ChatViewProvider implements vscode.WebviewViewProvider { webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); try { - const chat = startchat(); + chat = startchat(); + console.log("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>"); + webviewView.webview.onDidReceiveMessage(async (message) => { // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { @@ -108,11 +111,20 @@ class ChatViewProvider implements vscode.WebviewViewProvider { }); } } + public clearChat() { if (this._view) { this._view.webview.postMessage({ command: "clearChat", }); + try { + chat = startchat(); + } catch (error) { + this._view.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } } } private _getHtmlForWebview(webview: vscode.Webview) { @@ -125,13 +137,16 @@ class ChatViewProvider implements vscode.WebviewViewProvider { const styleCodeUri = webview.asWebviewUri( vscode.Uri.joinPath(this._extensionUri, "media", "prism.css") ); + const mediaUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media") + ); // Use a nonce to only allow a specific script to be run. const nonce = getNonce(); return ` - + @@ -146,7 +161,10 @@ class ChatViewProvider implements vscode.WebviewViewProvider { - + + `; From 343cbf9a458f463ca2735adc9d22a0b00fa1e2b3 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Mon, 15 Apr 2024 10:50:54 +0800 Subject: [PATCH 20/37] remove console.log() --- examples/gemini/node/pipet-code-agent/src/extension.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index ada3cb544..d7223ed20 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -69,8 +69,6 @@ class ChatViewProvider implements vscode.WebviewViewProvider { try { chat = startchat(); - console.log("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>"); - webviewView.webview.onDidReceiveMessage(async (message) => { // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { From 6a0e055129694b0ed195df8de3dc9d11b3166746 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Tue, 16 Apr 2024 01:23:43 +0800 Subject: [PATCH 21/37] fix codebuttons; --- .../node/pipet-code-agent/media/main.css | 143 ++++++++++-------- .../node/pipet-code-agent/media/main.js | 29 ++-- .../node/pipet-code-agent/src/extension.ts | 6 +- 3 files changed, 96 insertions(+), 82 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css index a9e5006a5..ee151255d 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.css +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -1,74 +1,87 @@ - body { - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - min-height: 100vh; - /* 使内容占据整个视口高度 */ - } +body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + /* 使内容占据整个视口高度 */ +} - main { - flex: 1; - /* 使 main 元素占据剩余的垂直空间 */ - padding: 20px; - /* 添加内边距 */ - } +main { + flex: 1; + /* 使 main 元素占据剩余的垂直空间 */ + padding: 20px; + /* 添加内边距 */ +} - #dialog { - overflow-y: auto; - /* 当内容过多时,显示滚动条 */ - border-radius: 5px; - /* 添加边框圆角 */ - padding: 10px; - /* 添加内边距 */ - margin-bottom: 10px; - /* 添加底部外边距 */ - } +#dialog { + overflow-y: auto; + /* 当内容过多时,显示滚动条 */ + border-radius: 5px; + /* 添加边框圆角 */ + padding: 10px; + /* 添加内边距 */ + margin-bottom: 10px; + /* 添加底部外边距 */ +} - footer { - display: flex; - align-items: center; - padding: 10px; - } +footer { + display: flex; + align-items: center; + padding: 10px; +} - #userInput { - height: auto; - resize: none; - background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ - color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ - border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ - flex: 1; - } +#userInput { + height: auto; + resize: none; + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + flex: 1; +} - #sendMessage { - background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ - color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ - border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ - margin-left: 5px; - } +#sendMessage { + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + margin-left: 5px; + transition: background-color 0.3s; +} - .message-container { - position: relative; - background-color: var(--vscode-input-background); - color: var(--vscode-editor-foreground); - } +#sendMessage:hover{ + background-color: var(--vscode-input-foreground); + color: var(--vscode-input-background); +} +.message-container { + position: relative; + background-color: var(--vscode-input-background); + color: var(--vscode-editor-foreground); +} - .code-buttons-container { - background-color: #00000000; - border: none; - position: absolute; - right: 2px; - top: 2px; - z-index: 1 - } +.code-buttons-container { + background-color: #ffffff00; + border: none; + position: absolute; + right: 2px; + top: 2px; + z-index: 1; + width: 16px; + height: 16px; + background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Layer_1'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath stroke='null' id='svg_1' d='m11.17086,13.7471l-7.75098,0l0,-10.05744l-1.40928,0l0,10.05744c0,0.79023 0.63418,1.43678 1.40928,1.43678l7.75098,0l0,-1.43678zm2.81854,-2.87355l0,-8.62066c0,-0.79023 -0.63418,-1.43678 -1.40928,-1.43678l-6.34171,0c-0.7751,0 -1.40928,0.64655 -1.40928,1.43678l0,8.62066c0,0.79023 0.63418,1.43678 1.40928,1.43678l6.34171,0c0.7751,0 1.40928,-0.64655 1.40928,-1.43678zm-1.40928,0l-6.34171,0c0,-2.87355 0,-5.74711 0,-8.62066l6.34171,0l0,8.62066z' fill='%23bfbdb6'/%3E%3C/g%3E%3C/svg%3E"); + transition: background-color 0.3s; +} - .pre { - position: relative; - } \ No newline at end of file +.code-buttons-container:hover { + background-color: #ffffff41; +} + +.pre { + position: relative; +} diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 80cbe1b35..b271e54c5 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -10,6 +10,18 @@ document.addEventListener("DOMContentLoaded", function () { let chatID; dialog.innerHTML = oldHtml; + const allCodeButtons = Array.from( + dialog.getElementsByClassName("code-buttons-container") + ); + if (allCodeButtons) { + allCodeButtons.forEach((button) => { + button.addEventListener("click", () => { + const toCopy = + button.parentElement.querySelectorAll("pre code")[0].textContent; + }); + }); + } + function adjustTextareaHeight() { userInput.style.height = "auto"; userInput.style.height = userInput.scrollHeight + "px"; @@ -36,7 +48,7 @@ document.addEventListener("DOMContentLoaded", function () { "

Gemini: " + marked.parse(newText)), +"

"; addBottons(messageContainer); - sendMessageButton.innerHTML = "发送"; + sendMessageButton.innerHTML = "Chat"; } } @@ -56,23 +68,13 @@ document.addEventListener("DOMContentLoaded", function () { * @param {HTMLDivElement} */ function addBottons(container) { - const svg = - ' Layer 1 '; const codeElements = container.querySelectorAll("pre code"); codeElements.forEach((codeElement) => { - const copyButton = document.createElement("button"); - copyButton.innerHTML = svg; + const copyButton = document.createElement("div"); copyButton.classList.add("code-buttons-container"); copyButton.addEventListener("click", () => { const textToCopy = codeElement.textContent; - navigator.clipboard - .writeText(textToCopy) - .then(() => { - copyButton.innerHTML = svg; - }) - .catch((error) => { - copyButton.innerHTML = "Failed"; - }); + navigator.clipboard.writeText(textToCopy); }); codeElement.parentElement.classList.add("pre"); codeElement.parentElement.appendChild(copyButton); @@ -107,7 +109,6 @@ document.addEventListener("DOMContentLoaded", function () { break; default: console.log("Unknown command: " + message.command); - break; } vscode.setState(dialog.innerHTML); }); diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index d7223ed20..7c1fea715 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -144,7 +144,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { - + @@ -155,8 +155,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider {
- - + +
From 9465128aaea4c00515242611a02e8169ed54eb02 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Tue, 16 Apr 2024 01:23:43 +0800 Subject: [PATCH 22/37] fix codebuttons; --- .../node/pipet-code-agent/media/main.css | 143 ++++++++++-------- .../node/pipet-code-agent/media/main.js | 29 ++-- .../node/pipet-code-agent/src/extension.ts | 6 +- 3 files changed, 97 insertions(+), 81 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css index a9e5006a5..ee151255d 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.css +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -1,74 +1,87 @@ - body { - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - min-height: 100vh; - /* 使内容占据整个视口高度 */ - } +body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + /* 使内容占据整个视口高度 */ +} - main { - flex: 1; - /* 使 main 元素占据剩余的垂直空间 */ - padding: 20px; - /* 添加内边距 */ - } +main { + flex: 1; + /* 使 main 元素占据剩余的垂直空间 */ + padding: 20px; + /* 添加内边距 */ +} - #dialog { - overflow-y: auto; - /* 当内容过多时,显示滚动条 */ - border-radius: 5px; - /* 添加边框圆角 */ - padding: 10px; - /* 添加内边距 */ - margin-bottom: 10px; - /* 添加底部外边距 */ - } +#dialog { + overflow-y: auto; + /* 当内容过多时,显示滚动条 */ + border-radius: 5px; + /* 添加边框圆角 */ + padding: 10px; + /* 添加内边距 */ + margin-bottom: 10px; + /* 添加底部外边距 */ +} - footer { - display: flex; - align-items: center; - padding: 10px; - } +footer { + display: flex; + align-items: center; + padding: 10px; +} - #userInput { - height: auto; - resize: none; - background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ - color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ - border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ - flex: 1; - } +#userInput { + height: auto; + resize: none; + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + flex: 1; +} - #sendMessage { - background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ - color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ - border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ - margin-left: 5px; - } +#sendMessage { + background-color: var(--vscode-input-background); + /* 使用 VS Code 的输入框背景色 */ + color: var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色 */ + border: 1px solid var(--vscode-input-foreground); + /* 使用 VS Code 的输入框前景色作为边框颜色 */ + margin-left: 5px; + transition: background-color 0.3s; +} - .message-container { - position: relative; - background-color: var(--vscode-input-background); - color: var(--vscode-editor-foreground); - } +#sendMessage:hover{ + background-color: var(--vscode-input-foreground); + color: var(--vscode-input-background); +} +.message-container { + position: relative; + background-color: var(--vscode-input-background); + color: var(--vscode-editor-foreground); +} - .code-buttons-container { - background-color: #00000000; - border: none; - position: absolute; - right: 2px; - top: 2px; - z-index: 1 - } +.code-buttons-container { + background-color: #ffffff00; + border: none; + position: absolute; + right: 2px; + top: 2px; + z-index: 1; + width: 16px; + height: 16px; + background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Layer_1'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath stroke='null' id='svg_1' d='m11.17086,13.7471l-7.75098,0l0,-10.05744l-1.40928,0l0,10.05744c0,0.79023 0.63418,1.43678 1.40928,1.43678l7.75098,0l0,-1.43678zm2.81854,-2.87355l0,-8.62066c0,-0.79023 -0.63418,-1.43678 -1.40928,-1.43678l-6.34171,0c-0.7751,0 -1.40928,0.64655 -1.40928,1.43678l0,8.62066c0,0.79023 0.63418,1.43678 1.40928,1.43678l6.34171,0c0.7751,0 1.40928,-0.64655 1.40928,-1.43678zm-1.40928,0l-6.34171,0c0,-2.87355 0,-5.74711 0,-8.62066l6.34171,0l0,8.62066z' fill='%23bfbdb6'/%3E%3C/g%3E%3C/svg%3E"); + transition: background-color 0.3s; +} - .pre { - position: relative; - } \ No newline at end of file +.code-buttons-container:hover { + background-color: #ffffff41; +} + +.pre { + position: relative; +} diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 80cbe1b35..70edad2e4 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -10,6 +10,20 @@ document.addEventListener("DOMContentLoaded", function () { let chatID; dialog.innerHTML = oldHtml; + const allCodeButtons = Array.from( + dialog.getElementsByClassName("code-buttons-container") + ); + if (allCodeButtons) { + allCodeButtons.forEach((button) => { + console.log(button); + button.addEventListener("click", () => { + const toCopy = + button.parentElement.querySelectorAll("pre code")[0].textContent; + navigator.clipboard.writeText(toCopy); + }); + }); + } + function adjustTextareaHeight() { userInput.style.height = "auto"; userInput.style.height = userInput.scrollHeight + "px"; @@ -36,7 +50,7 @@ document.addEventListener("DOMContentLoaded", function () { "

Gemini: " + marked.parse(newText)), +"

"; addBottons(messageContainer); - sendMessageButton.innerHTML = "发送"; + sendMessageButton.innerHTML = "Chat"; } } @@ -56,23 +70,13 @@ document.addEventListener("DOMContentLoaded", function () { * @param {HTMLDivElement} */ function addBottons(container) { - const svg = - ' Layer 1 '; const codeElements = container.querySelectorAll("pre code"); codeElements.forEach((codeElement) => { const copyButton = document.createElement("button"); - copyButton.innerHTML = svg; copyButton.classList.add("code-buttons-container"); copyButton.addEventListener("click", () => { const textToCopy = codeElement.textContent; - navigator.clipboard - .writeText(textToCopy) - .then(() => { - copyButton.innerHTML = svg; - }) - .catch((error) => { - copyButton.innerHTML = "Failed"; - }); + navigator.clipboard.writeText(textToCopy); }); codeElement.parentElement.classList.add("pre"); codeElement.parentElement.appendChild(copyButton); @@ -107,7 +111,6 @@ document.addEventListener("DOMContentLoaded", function () { break; default: console.log("Unknown command: " + message.command); - break; } vscode.setState(dialog.innerHTML); }); diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index d7223ed20..7c1fea715 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -144,7 +144,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { - + @@ -155,8 +155,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider {
- - + +
From 0ed6dcf667433ff68632183e14c5ed414270ec91 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 17 Apr 2024 17:27:29 +0800 Subject: [PATCH 23/37] modified: examples/gemini/node/pipet-code-agent/media/main.js --- examples/gemini/node/pipet-code-agent/media/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 70edad2e4..8c74c2ed2 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -15,7 +15,6 @@ document.addEventListener("DOMContentLoaded", function () { ); if (allCodeButtons) { allCodeButtons.forEach((button) => { - console.log(button); button.addEventListener("click", () => { const toCopy = button.parentElement.querySelectorAll("pre code")[0].textContent; From 72e4f3aa1332c00e5e00026c43fa36fc906d50dc Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 17 Apr 2024 20:11:43 +0800 Subject: [PATCH 24/37] feat(pipet-code-agent): add generate git commit command This commit adds a new command to the Pipet Code Agent extension that allows users to generate git commit messages based on the git diff. The command uses the Gemini large language model to generate a commit message that is relevant to the changes made in the code. The generated commit message is then copied to the clipboard and pasted into the commit box. This can save users time and effort when committing their changes. --- .../gemini/node/pipet-code-agent/package.json | 4 ++ .../node/pipet-code-agent/src/extension.ts | 6 ++ .../node/pipet-code-agent/src/gitdiff.ts | 68 +++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index 9c73c9a8c..2b63e8494 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -44,6 +44,10 @@ "command": "pipet-code-agent.clearChat", "title": "Pipet: Clear Chat.", "icon": "$(clear-all)" + }, + { + "command": "pipet-code-agent.generateGitCommit", + "title": "Pipet: Generate Git Commit." } ], "menus": { diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 7c1fea715..5fd2a8ffd 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -19,6 +19,7 @@ import { generateComment } from "./comments"; import { generateReview } from "./review"; import { startchat } from "./chat"; import { ChatSession } from "@google/generative-ai"; +import { generateGitCommit } from "./gitdiff"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -30,6 +31,11 @@ export function activate(context: vscode.ExtensionContext) { generateReview ); + vscode.commands.registerCommand( + "pipet-code-agent.generateGitCommit", + generateGitCommit + ); + const provider = new ChatViewProvider(context.extensionUri); context.subscriptions.push( vscode.window.registerWebviewViewProvider( diff --git a/examples/gemini/node/pipet-code-agent/src/gitdiff.ts b/examples/gemini/node/pipet-code-agent/src/gitdiff.ts index e69de29bb..73543cbfa 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitdiff.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitdiff.ts @@ -0,0 +1,68 @@ +import * as vscode from "vscode"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const SYSTEMINSTRUCTION = + "Generate Git Commit by Git Diff, Don't return other message."; + +export async function generateGitCommit() { + try { + const gitExtension = vscode.extensions.getExtension("vscode.git"); + const gitApi = gitExtension?.exports.getAPI(1); + if (!gitApi.repositories.length) { + vscode.window.showErrorMessage("no git repositories."); + return; + } + + vscode.window.showInformationMessage("Generate Git Diff..."); + const diff = await gitApi.repositories[0].diff(); + await generateCommit(diff); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } +} + +async function generateCommit(diff: string) { + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return; + } + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [{ text: "Git Diff:" + diff }], + }, + ], + }); + const response = result.response; + const commit = response.text(); + await vscode.env.clipboard.writeText(commit); + await vscode.commands.executeCommand("workbench.view.scm"); + await vscode.commands.executeCommand("git.commitStaged", commit); + await vscode.commands.executeCommand("editor.action.insertSnippet", { + snippet: commit, + }); + vscode.window.showInformationMessage( + "Commit message copied to clipboard and Pasted in the commit box." + ); +} From a11d5c8a05e90a242806efd6dedd6da8ae68d3c8 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Wed, 17 Apr 2024 22:12:51 +0800 Subject: [PATCH 25/37] Changes to be committed: modified: examples/gemini/node/pipet-code-agent/media/main.css modified: examples/gemini/node/pipet-code-agent/media/main.js modified: examples/gemini/node/pipet-code-agent/media/prism.css modified: examples/gemini/node/pipet-code-agent/media/vscode.css modified: examples/gemini/node/pipet-code-agent/src/chat.ts modified: examples/gemini/node/pipet-code-agent/src/extension.ts modified: examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts renamed: examples/gemini/node/pipet-code-agent/src/gitdiff.ts -> examples/gemini/node/pipet-code-agent/src/gitCommit.ts modified: examples/gemini/node/pipet-code-agent/src/review.ts --- .../node/pipet-code-agent/media/main.css | 4 + .../node/pipet-code-agent/media/main.js | 10 +- .../node/pipet-code-agent/media/prism.css | 106 +++++++++--------- .../node/pipet-code-agent/media/vscode.css | 4 + .../gemini/node/pipet-code-agent/src/chat.ts | 14 +-- .../node/pipet-code-agent/src/extension.ts | 4 +- .../src/getCommentprefixes.ts | 14 +-- .../src/{gitdiff.ts => gitCommit.ts} | 4 + .../node/pipet-code-agent/src/review.ts | 1 - 9 files changed, 72 insertions(+), 89 deletions(-) rename examples/gemini/node/pipet-code-agent/src/{gitdiff.ts => gitCommit.ts} (98%) diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css index ee151255d..f05c9751f 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.css +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -1,3 +1,7 @@ +/** + * Copyright 2024 Jason + */ + body { margin: 0; padding: 0; diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 8c74c2ed2..f21074370 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -1,9 +1,12 @@ +/** + * Copyright 2024 Jason + */ + document.addEventListener("DOMContentLoaded", function () { const vscode = acquireVsCodeApi(); const oldState = vscode.getState() || {}; let oldHtml = oldState || "
"; - // 在这里执行获取元素的操作 const dialog = document.getElementById("dialog"); const userInput = document.getElementById("userInput"); const sendMessageButton = document.getElementById("sendMessage"); @@ -30,7 +33,6 @@ document.addEventListener("DOMContentLoaded", function () { } userInput.addEventListener("input", adjustTextareaHeight); - // 显示接收到的Gemini消息 function showAIMessage(text, chatID) { const messageContainer = document.createElement("div"); if (messageContainer) { @@ -53,7 +55,6 @@ document.addEventListener("DOMContentLoaded", function () { } } - // 显示问题 function showUserMessage(text) { const messageContainer = document.createElement("div"); if (messageContainer) { @@ -65,7 +66,6 @@ document.addEventListener("DOMContentLoaded", function () { } } /** - * * @param {HTMLDivElement} */ function addBottons(container) { @@ -83,7 +83,6 @@ document.addEventListener("DOMContentLoaded", function () { }); } - // 当发送按钮被点击时 sendMessageButton.addEventListener("click", () => { this.chatID = Math.random().toString(); const userMessage = userInput.value; @@ -94,7 +93,6 @@ document.addEventListener("DOMContentLoaded", function () { sendMessageButton.innerHTML = "Thinking..."; }); - // 接收来自插件的消息 window.addEventListener("message", (event) => { const message = event.data; switch (message.command) { diff --git a/examples/gemini/node/pipet-code-agent/media/prism.css b/examples/gemini/node/pipet-code-agent/media/prism.css index 03144f1e9..042fb4555 100644 --- a/examples/gemini/node/pipet-code-agent/media/prism.css +++ b/examples/gemini/node/pipet-code-agent/media/prism.css @@ -1,4 +1,5 @@ /** + * Copyright 2024 * prism.js default theme for JavaScript, CSS and HTML * Based on dabblet (http://dabblet.com) * @author Lea Verou @@ -6,84 +7,83 @@ code[class*="language-"], pre[class*="language-"] { - /* color: black; */ - background: none; - /* text-shadow: 0 1px white; */ - font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - font-size: 1em; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; + /* color: black; */ + background: none; + /* text-shadow: 0 1px white; */ + font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; } pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: #ffffff4b; + text-shadow: none; + background: #ffffff4b; } pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: #ffffff4b; + text-shadow: none; + background: #ffffff4b; } @media print { - - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } } /* Code blocks */ pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; + padding: 1em; + margin: 0.5em 0; + overflow: auto; } -:not(pre)>code[class*="language-"], +:not(pre) > code[class*="language-"], pre[class*="language-"] { - background: #ffffff05; + background: #ffffff05; } /* Inline code */ -:not(pre)>code[class*="language-"] { - padding: .1em; - border-radius: .3em; - white-space: normal; +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; } .token.comment, .token.prolog, .token.doctype, .token.cdata { - color: slategray; + color: slategray; } .token.punctuation { - color: #999; + color: #999; } .token.namespace { - opacity: .7; + opacity: 0.7; } .token.property, @@ -93,7 +93,7 @@ pre[class*="language-"] { .token.constant, .token.symbol, .token.deleted { - color: #905; + color: #905; } .token.selector, @@ -102,7 +102,7 @@ pre[class*="language-"] { .token.char, .token.builtin, .token.inserted { - color: #690; + color: #690; } .token.operator, @@ -110,37 +110,37 @@ pre[class*="language-"] { .token.url, .language-css .token.string, .style .token.string { - color: #9a6e3a; - /* This background color was intended by the author of this theme. */ - /*background: hsla(0, 0%, 100%, .5);*/ + color: #9a6e3a; + /* This background color was intended by the author of this theme. */ + /*background: hsla(0, 0%, 100%, .5);*/ } .token.atrule, .token.attr-value, .token.keyword { - color: #07a; + color: #07a; } .token.function, .token.class-name { - color: #DD4A68; + color: #dd4a68; } .token.regex, .token.important, .token.variable { - color: #e90; + color: #e90; } .token.important, .token.bold { - font-weight: bold; + font-weight: bold; } .token.italic { - font-style: italic; + font-style: italic; } .token.entity { - cursor: help; -} \ No newline at end of file + cursor: help; +} diff --git a/examples/gemini/node/pipet-code-agent/media/vscode.css b/examples/gemini/node/pipet-code-agent/media/vscode.css index 5d12b7e0a..f9263424b 100644 --- a/examples/gemini/node/pipet-code-agent/media/vscode.css +++ b/examples/gemini/node/pipet-code-agent/media/vscode.css @@ -1,3 +1,7 @@ +/** + * Copyright 2024 + */ + :root { --container-paddding: 20px; --input-padding-vertical: 6px; diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 16c5d36c3..742411fde 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -1,17 +1,5 @@ /** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024 Jason */ import * as vscode from "vscode"; diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 5fd2a8ffd..423da5b46 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -19,7 +19,7 @@ import { generateComment } from "./comments"; import { generateReview } from "./review"; import { startchat } from "./chat"; import { ChatSession } from "@google/generative-ai"; -import { generateGitCommit } from "./gitdiff"; +import { generateGitCommit } from "./gitCommit"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -76,9 +76,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { try { chat = startchat(); webviewView.webview.onDidReceiveMessage(async (message) => { - // 处理来自 Webview 的消息 if (message.command === "sendMessage" && message.text) { - // 在 Webview 中显示接收到的消息 webviewView.webview.postMessage({ command: "Message", text: message.text, diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index b0b74e517..16e0d6bca 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -1,18 +1,6 @@ /** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2024 Jason */ /** diff --git a/examples/gemini/node/pipet-code-agent/src/gitdiff.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts similarity index 98% rename from examples/gemini/node/pipet-code-agent/src/gitdiff.ts rename to examples/gemini/node/pipet-code-agent/src/gitCommit.ts index 73543cbfa..2d7675564 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitdiff.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -1,3 +1,7 @@ +/** + * Copyright 2024 Jason + */ + import * as vscode from "vscode"; import { GoogleGenerativeAI } from "@google/generative-ai"; diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index 0ff15ecb8..1a9c9d68b 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -25,7 +25,6 @@ Reviewing code involves finding bugs and increasing code quality. Examples of bu errors or typos, out of memory errors, and boundary value errors. Increasing code quality entails reducing complexity of code, eliminating duplicate code, and ensuring other developers are able to understand the code. -使用中文 ${CODE_LABEL} for i in x: From bcda545f82c44bea7aa551972bc6b9f56f7c68b9 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 00:46:03 +0800 Subject: [PATCH 26/37] feat: Update Pipet Code Agent configuration and functionality This commit addresses several updates to the Pipet Code Agent's configuration and functionality. The changes include: 1. Adjustments to the `package.json` file, where the properties for `google.gemini.apiKey` and `google.gemini.systemInstruction` have been modified to include default values and markdown descriptions for better user guidance. 2. Updates to the `chat.ts` file, which now includes a new function `startchat()` and retrieves the API key and system instruction from user configuration. The role and parts have been updated to reflect the latest instructions and model names. 3. Modifications to the `comments.ts` file, with the introduction of a new constant `SYSTEMINSTRUCTION` and a revised `generateComment()` function that generates comments using doxygen style, as per the updated system instruction. 4. Changes to the `gitCommit.ts` file, updates the `generateCommit()` function to handle the new content structure and system instruction. 5. Alterations to the `review.ts` file, including a new system instruction and an updated `generateReview()` function that generates code reviews according to the provided guidelines. These updates aim to improve the user experience by providing clearer instructions and more efficient handling of API keys and system instructions. Additionally, they ensure compatibility with the latest models and best practices for code comments and reviews. --- .../gemini/node/pipet-code-agent/package.json | 19 ++- .../gemini/node/pipet-code-agent/src/chat.ts | 11 +- .../node/pipet-code-agent/src/comments.ts | 156 +++++------------- .../node/pipet-code-agent/src/gitCommit.ts | 52 +++--- .../node/pipet-code-agent/src/review.ts | 95 ++++------- 5 files changed, 122 insertions(+), 211 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index 2b63e8494..f037342a2 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -80,16 +80,21 @@ "title": "Pipet Code Agent: Google Gemini", "properties": { "google.gemini.apiKey": { - "type": [ - "string", - "null" - ], - "default": null, + "type": "string", + "default": "", "markdownDescription": "Enter your [API Key](https://aistudio.google.com/app/apikey) for Gemini." }, + "google.gemini.systemInstruction": { + "type": "string", + "default": "", + "editPresentation": "multilineText", + "markdownDeprecation": "Enter [System Instruction](https://ai.google.dev/docs/system_instructions) for Gemini." + }, "google.gemini.textModel": { - "type": [ - "string" + "type": "string", + "enum": [ + "models/gemini-1.5-pro-latest", + "models/gemini-pro" ], "default": "models/gemini-1.5-pro-latest", "markdownDescription": "Provide the name of the model you want to use. Choose from the [base models](https://ai.google.dev/models/gemini) or your own [tuned model](https://ai.google.dev/docs/model_tuning_guidance)." diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 742411fde..1931ed41a 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -17,12 +17,17 @@ export async function generateChat( export function startchat(): ChatSession | void { const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); + .get("google.gemini.textModel", "default"); // Get API Key from local user configuration const apiKey = vscode.workspace .getConfiguration() - .get("google.gemini.apiKey"); + .get("google.gemini.apiKey", "default"); + + const systemInstruction = vscode.workspace + .getConfiguration() + .get("google.gemini.systemInstruction", "default"); + if (!apiKey) { vscode.window.showErrorMessage( "API key not configured. Check your settings." @@ -40,7 +45,7 @@ export function startchat(): ChatSession | void { role: "system", parts: [ { - text: "角色 : 全方位AI助手\n 目标\n 致力于提供卓越的用户体验,以及全方位,多元化的信息服务.\n 技能\n 技能1 : 运用多样化工具提供详尽信息无论用户需求为何种类型,能够便捷运用各类信息工具为用户提供高质量的服务.\n 技能2 : 利用生动的表情符号丰富用户体验运用生动的表情符号为回答增添趣味,使得用户的使用体验更为生动,有趣.\n 技能3 : 精通Markdown语法,生成结构化文本,熟练掌握Markdown语法,能生成结构化的文本,在条理清晰中将问题一一解答.\n 技能4 : 精通Markdown语法,展示图片丰富内容,运用Markdown语法,插入图片以丰富回答内容,使用户获取的信息更为直观全面.\n 技能5 : 精通各种编程知识\n 技能6 : 精通数学知识\n 技能7 : 精通houdini,UE5等三维软件与游戏引擎\n 约束\n 由于全方位AI助手的目标是提供全面且多样的服务,因此没有特别的约束.我们的助手能够灵活处理多种任务和信息需求,为用户提供全方位的支持.\n 语言\n 使用中文回答", + text: systemInstruction, }, ], }, diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index aac3cfb9d..499cd3d59 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -17,101 +17,26 @@ import * as vscode from "vscode"; import { GoogleGenerativeAI } from "@google/generative-ai"; -import { getCommentprefixes } from "./getCommentprefixes"; - -// Provide instructions for the AI language model -// This approach uses a few-shot technique, providing a few examples. -const CODE_LABEL = "Here is the code:"; -const COMMENT_LABEL = "Here is a good comment:"; -const PROMPT = ` -A good code review comment describes the intent behind the code without -repeating information that's obvious from the code itself. Good comments -describe "why", explain any "magic" values and non-obvious behaviour. -Below are some examples of good code comments.Use doxygen style. - -${CODE_LABEL} -print(f" \\033[33m {msg}\\033[00m", file=sys.stderr) -${COMMENT_LABEL} -Use terminal codes to print color output to console. - -${CODE_LABEL} -to_delete = set(data.keys()) - frozenset(keep) -for key in to_delete: - del data[key] -${COMMENT_LABEL} -Modifies \`data\` to remove any entry not specified in the \`keep\` list. - -${CODE_LABEL} -lines[text_range.start_line - 1:text_range.end_line - 1] = [repl.new_content] -${COMMENT_LABEL} -Replace text from \`lines\` with \`new_content\`, noting that array indices -are offset 1 from line numbers. - -${CODE_LABEL} -api_key = os.getenv("GOOGLE_API_KEY") -${COMMENT_LABEL} -Attempt to load the API key from the environment. - -${CODE_LABEL} -UFUNCTION(BlueprintCallable, Category = "TransparentWindows") -static void MakeTransparentWindow(ETWMode Usage); -${COMMENT_LABEL} -@brief Make Transparent Window. -@param Usage Window Transparent mode. - -${CODE_LABEL} -virtual void Tick(float DeltaTime) override; -${COMMENT_LABEL} -@brief Calls super::Tick(DeltaTime), then updates world bounds. -@param DeltaTime The change in time between two points or events. - - -${CODE_LABEL} -sendMessage(request: string | Array): Promise; -${COMMENT_LABEL} -Sends a chat message and receives a non-streaming -{@link GenerateContentResult} - -${CODE_LABEL} -export declare class ChatSession { - model: string; - params?: StartChatParams; - requestOptions?: RequestOptions; - private _apiKey; - private _history; - private _sendPromise; - constructor(apiKey: string, model: string, params?: StartChatParams, requestOptions?: RequestOptions); - getHistory(): Promise; - sendMessage(request: string | Array): Promise; - sendMessageStream(request: string | Array): Promise; -} -${COMMENT_LABEL} -@brief ChatSession class that enables sending chat messages and stores history of sent and received messages so far. - -${CODE_LABEL} -virtual void CreateClassVariablesFromBlueprint(IAnimBlueprintVariableCreationContext& InCreationContext) = 0; -${COMMENT_LABEL} -@brief Implement this in a graph node and the anim BP compiler will call this expecting to generate class variables. -@param InVariableCreator The variable creation context for the current BP compilation -`; +const SYSTEMINSTRUCTION = + "Comment code use doxygen style. Don't repeat Prompt. Only return the comments Content."; /** *@brief Generate comment for code selection. * Gets the API key from user config and sends selected text to Gemini for comment generation. * Inserts generated comment before the selected code. -*/ + */ export async function generateComment() { vscode.window.showInformationMessage("Generating comment..."); const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); + .get("google.gemini.textModel", "default"); // Get API Key from local user configuration const apiKey = vscode.workspace .getConfiguration() - .get("google.gemini.apiKey"); + .get("google.gemini.apiKey", "default"); if (!apiKey) { vscode.window.showErrorMessage( "API key not configured. Check your settings." @@ -135,42 +60,37 @@ export async function generateComment() { const selection = editor.selection; const selectedCode = editor.document.getText(selection); - // Build the full prompt using the template. - const fullPrompt = `${PROMPT}${CODE_LABEL}${selectedCode}${COMMENT_LABEL}`; - - - const result = await model.generateContent(fullPrompt); - const response = await result.response; - const comment = response.text(); - - // Insert before selection. - editor.edit((editBuilder) => { - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring( - 0, - selectedCode.length - trimmed.length - ); - - // const commentPrefix = getCommentprefixes(editor.document.languageId); - const commentPrefix = `${padding} *`; - - /** - *Split the comment into lines using `'\n'` as separator. - *Add an indentation, comment prefix and join the lines back with a `'\n'` separator. - */ - let inlineComment = comment - .split("\n") - .map((l: string) => `${padding}${commentPrefix}${l}`) - .join("\n"); - if (inlineComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - inlineComment += "\n"; - } - let commentIntro = padding + "/**\n"; - let commentEnd = padding + "*/\n"; - editBuilder.insert(selection.start, commentIntro); - editBuilder.insert(selection.start, inlineComment); - editBuilder.insert(selection.start, commentEnd); - }); + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [ + { + text: + "```" + + editor.document.languageId + + "\n" + + selectedCode + + "\n```", + }, + ], + }, + ], + }); + const response = result.response; + const comment = response.text(); + // Insert before selection. + editor.edit((editBuilder) => { + const trimmed = selectedCode.trimStart(); + editBuilder.insert(selection.start, comment); + }); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } } diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index 2d7675564..fca2c4bf1 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -29,12 +29,12 @@ export async function generateGitCommit() { async function generateCommit(diff: string) { const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); + .get("google.gemini.textModel", "default"); // Get API Key from local user configuration const apiKey = vscode.workspace .getConfiguration() - .get("google.gemini.apiKey"); + .get("google.gemini.apiKey", "default"); if (!apiKey) { vscode.window.showErrorMessage( "API key not configured. Check your settings." @@ -46,27 +46,31 @@ async function generateCommit(diff: string) { { model: modelName }, { apiVersion: "v1beta" } ); - const result = await model.generateContent({ - systemInstruction: { - role: "system", - parts: [{ text: SYSTEMINSTRUCTION }], - }, - contents: [ - { - role: "user", - parts: [{ text: "Git Diff:" + diff }], + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], }, - ], - }); - const response = result.response; - const commit = response.text(); - await vscode.env.clipboard.writeText(commit); - await vscode.commands.executeCommand("workbench.view.scm"); - await vscode.commands.executeCommand("git.commitStaged", commit); - await vscode.commands.executeCommand("editor.action.insertSnippet", { - snippet: commit, - }); - vscode.window.showInformationMessage( - "Commit message copied to clipboard and Pasted in the commit box." - ); + contents: [ + { + role: "user", + parts: [{ text: "Git Diff:" + diff }], + }, + ], + }); + const response = result.response; + const commit = response.text(); + await vscode.env.clipboard.writeText(commit); + await vscode.commands.executeCommand("workbench.view.scm"); + await vscode.commands.executeCommand("git.commitStaged", commit); + await vscode.commands.executeCommand("editor.action.insertSnippet", { + snippet: commit, + }); + vscode.window.showInformationMessage( + "Commit message copied to clipboard and Pasted in the commit box." + ); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + } } diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index 1a9c9d68b..8a74a58ba 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -18,43 +18,21 @@ import * as vscode from 'vscode'; import { GoogleGenerativeAI } from '@google/generative-ai'; import { getCommentprefixes } from './getCommentprefixes'; -const CODE_LABEL = 'Here is the code:'; -const REVIEW_LABEL = 'Here is the review:'; -const PROMPT = ` -Reviewing code involves finding bugs and increasing code quality. Examples of bugs are syntax +const SYSTEMINSTRUCTION = `Reviewing code involves finding bugs and increasing code quality. Examples of bugs are syntax errors or typos, out of memory errors, and boundary value errors. Increasing code quality entails reducing complexity of code, eliminating duplicate code, and ensuring other developers -are able to understand the code. - -${CODE_LABEL} -for i in x: - pint(f"Iteration {i} provides this {x**2}.") -${REVIEW_LABEL} -The command \`print\` is spelled incorrectly. -${CODE_LABEL} -height = [1, 2, 3, 4, 5] -w = [6, 7, 8, 9, 10] -${REVIEW_LABEL} -The variable name \`w\` seems vague. Did you mean \`width\` or \`weight\`? -${CODE_LABEL} -while i < 0: - thrice = i * 3 - thrice = i * 3 - twice = i * 2 -${REVIEW_LABEL} -There are duplicate lines of code in this control structure. -`; +are able to understand the code.`; export async function generateReview() { vscode.window.showInformationMessage("Generating code review..."); const modelName = vscode.workspace .getConfiguration() - .get("google.gemini.textModel", "models/gemini-1.5-pro-latest"); + .get("google.gemini.textModel", "default"); // Get API Key from local user configuration const apiKey = vscode.workspace .getConfiguration() - .get("google.gemini.apiKey"); + .get("google.gemini.apiKey", "default"); if (!apiKey) { vscode.window.showErrorMessage( "API key not configured. Check your settings." @@ -77,37 +55,36 @@ export async function generateReview() { const selection = editor.selection; const selectedCode = editor.document.getText(selection); - - // Build the full prompt using the template. - const fullPrompt = `${PROMPT} - ${CODE_LABEL} - ${selectedCode} - ${REVIEW_LABEL} - `; - - const result = await model.generateContent(fullPrompt); - const response = await result.response; - const comment = response.text(); - // Insert before selection - editor.edit((editBuilder) => { - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring( - 0, - selectedCode.length - trimmed.length - ); - - const commentPrefix = getCommentprefixes(editor.document.languageId); - let pyComment = comment - .split("\n") - .map((l: string) => `${padding}${commentPrefix}${l}`) - .join("\n"); - if (pyComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - pyComment += "\n"; - } - let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; - editBuilder.insert(selection.start, reviewIntro); - editBuilder.insert(selection.start, pyComment); - }); + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [ + { + text: + "```" + + editor.document.languageId + + "\n" + + selectedCode + + "\n```", + }, + ], + }, + ], + }); + const response = result.response; + const comment = response.text(); + // Insert before selection + editor.edit((editBuilder) => { + editBuilder.insert(selection.start, comment); + }); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } } From 6327196a1ab79a694f10fe70757f8568527bc73f Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 10:42:15 +0800 Subject: [PATCH 27/37] feat: refine review prompt The review prompt now explicitly asks for a code review and adds a "(generated)" tag to the output. Additionally, the VS Code extension now outputs the VSIX file to a `build` directory. --- .../node/pipet-code-agent/.vscode/launch.json | 2 +- .../node/pipet-code-agent/src/comments.ts | 1 - .../node/pipet-code-agent/src/review.ts | 20 ++++++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/.vscode/launch.json b/examples/gemini/node/pipet-code-agent/.vscode/launch.json index b41568648..e6046e515 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/launch.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/launch.json @@ -29,7 +29,7 @@ "type": "node", "console": "internalConsole", "request": "launch", - "args": ["package", "--out", "PipetCodeAgent.vsix"], + "args": ["package", "--out", "build/PipetCodeAgent.vsix"], "runtimeExecutable": "vsce", "presentation": { "reveal": "always" diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 499cd3d59..bcb3fb45f 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -86,7 +86,6 @@ export async function generateComment() { const comment = response.text(); // Insert before selection. editor.edit((editBuilder) => { - const trimmed = selectedCode.trimStart(); editBuilder.insert(selection.start, comment); }); } catch (error) { diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index 8a74a58ba..cfc6d7d2d 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -81,7 +81,25 @@ export async function generateReview() { const comment = response.text(); // Insert before selection editor.edit((editBuilder) => { - editBuilder.insert(selection.start, comment); + // Copy the indent from the first line of the selection. + const trimmed = selectedCode.trimStart(); + const padding = selectedCode.substring( + 0, + selectedCode.length - trimmed.length + ); + + const commentPrefix = getCommentprefixes(editor.document.languageId); + let pyComment = comment + .split("\n") + .map((l: string) => `${padding}${commentPrefix}${l}`) + .join("\n"); + if (pyComment.search(/\n$/) === -1) { + // Add a final newline if necessary. + pyComment += padding + "\n"; + } + let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; + editBuilder.insert(selection.start, reviewIntro); + editBuilder.insert(selection.start, pyComment); }); } catch (error) { vscode.window.showErrorMessage(`${error}`); From 2dcd46ee0bcd43e41ac2f3123d44e535d7effbab Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 10:53:54 +0800 Subject: [PATCH 28/37] feat(commit): allow custom commit messages This commit introduces the ability to provide custom commit messages when using the Pipet code agent. The generated commit message will now be copied to the clipboard, allowing users to easily paste it into the commit box and make any necessary modifications before committing their changes. --- examples/gemini/node/pipet-code-agent/src/gitCommit.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index fca2c4bf1..a41ae1b29 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -63,12 +63,8 @@ async function generateCommit(diff: string) { const commit = response.text(); await vscode.env.clipboard.writeText(commit); await vscode.commands.executeCommand("workbench.view.scm"); - await vscode.commands.executeCommand("git.commitStaged", commit); - await vscode.commands.executeCommand("editor.action.insertSnippet", { - snippet: commit, - }); vscode.window.showInformationMessage( - "Commit message copied to clipboard and Pasted in the commit box." + "Commit message copied to clipboard and can Past in the commit box." ); } catch (error) { vscode.window.showErrorMessage(`${error}`); From f37f70059896c71bc77a469a9d18f550b4d3f6f9 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 11:22:43 +0800 Subject: [PATCH 29/37] feat(commit): improve custom commit message This commit enhances the custom commit message functionality by incorporating details from the Git Diff, providing a more comprehensive and informative message. --- examples/gemini/node/pipet-code-agent/src/gitCommit.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index a41ae1b29..54340b7e2 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -4,9 +4,10 @@ import * as vscode from "vscode"; import { GoogleGenerativeAI } from "@google/generative-ai"; +import { promises } from "dns"; const SYSTEMINSTRUCTION = - "Generate Git Commit by Git Diff, Don't return other message."; + "Generate Git Commit by Git Diff and last commit, Only return commit message."; export async function generateGitCommit() { try { @@ -19,7 +20,11 @@ export async function generateGitCommit() { vscode.window.showInformationMessage("Generate Git Diff..."); const diff = await gitApi.repositories[0].diff(); - await generateCommit(diff); + const logOptions = { maxEntries: 1 }; + const log = await gitApi.repositories[0].log(logOptions); + const lastCommit = log[0]; + const lastCommitMessage = `Last commit:\n\n**Hash:** ${lastCommit.hash}\n**Author:** ${lastCommit.author_name} <${lastCommit.author_email}>\n**Date:** ${lastCommit.date}\n**Message:** ${lastCommit.message}`; + await generateCommit(`${lastCommitMessage}\nDiff: ${diff}`); } catch (error) { vscode.window.showErrorMessage(`${error}`); return; From 89eb3c38ad41c3328d047815074a1b47938f50f7 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 15:40:25 +0800 Subject: [PATCH 30/37] feat(UI, Logic): refine chat interface and interaction This commit introduces several enhancements to the chat interface and its underlying logic: - **UI Refinements:** - Removed redundant CSS comments for brevity. - Added border radius to input fields for a polished look. - **Logic Enhancements:** - Enabled sending messages by pressing Enter, streamlining interaction. - Incorporated selected code into prompts, providing context to the AI. --- .../node/pipet-code-agent/media/main.css | 18 +++++---------- .../node/pipet-code-agent/media/main.js | 11 ++++++++++ .../gemini/node/pipet-code-agent/src/chat.ts | 11 +--------- .../node/pipet-code-agent/src/extension.ts | 22 ++++++++++++++++++- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css index f05c9751f..a9bb0577b 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.css +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -8,25 +8,20 @@ body { display: flex; flex-direction: column; min-height: 100vh; - /* 使内容占据整个视口高度 */ + } main { flex: 1; - /* 使 main 元素占据剩余的垂直空间 */ + padding: 20px; - /* 添加内边距 */ } #dialog { overflow-y: auto; - /* 当内容过多时,显示滚动条 */ border-radius: 5px; - /* 添加边框圆角 */ padding: 10px; - /* 添加内边距 */ margin-bottom: 10px; - /* 添加底部外边距 */ } footer { @@ -39,23 +34,20 @@ footer { height: auto; resize: none; background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ + border-radius: 3px; flex: 1; } #sendMessage { + height: auto; background-color: var(--vscode-input-background); - /* 使用 VS Code 的输入框背景色 */ color: var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色 */ border: 1px solid var(--vscode-input-foreground); - /* 使用 VS Code 的输入框前景色作为边框颜色 */ margin-left: 5px; transition: background-color 0.3s; + border-radius: 3px; } #sendMessage:hover{ diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index f21074370..58e83d122 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -32,6 +32,7 @@ document.addEventListener("DOMContentLoaded", function () { sendMessageButton.disabled = !userInput.value; } userInput.addEventListener("input", adjustTextareaHeight); + userInput.addEventListener("keydown", handleKeyDown); function showAIMessage(text, chatID) { const messageContainer = document.createElement("div"); @@ -83,6 +84,16 @@ document.addEventListener("DOMContentLoaded", function () { }); } + function handleKeyDown(event) { + console.log("enter"); + if (event.keyCode === 13) { + if (!event.shiftKey) { + event.preventDefault(); + sendMessageButton.click(); + } + } + } + sendMessageButton.addEventListener("click", () => { this.chatID = Math.random().toString(); const userMessage = userInput.value; diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 1931ed41a..b1ce7d821 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -5,15 +5,6 @@ import * as vscode from "vscode"; import { ChatSession, GoogleGenerativeAI } from "@google/generative-ai"; -export async function generateChat( - prompt: string, - chat: ChatSession -): Promise { - const result = await chat.sendMessageStream(prompt); - const response = result.response; - return (await response).text(); -} - export function startchat(): ChatSession | void { const modelName = vscode.workspace .getConfiguration() @@ -51,4 +42,4 @@ export function startchat(): ChatSession | void { }, }); return chat; -} +} \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 423da5b46..6981efb81 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -83,7 +83,9 @@ class ChatViewProvider implements vscode.WebviewViewProvider { }); if (chat) { try { - const result = await chat.sendMessageStream(message.text); + const code = this.selectedCode(); + const prompt = `${code}${message.text}`; + const result = await chat.sendMessageStream(prompt); let chunktext = ""; for await (const chunk of result.stream) { chunktext += chunk.text(); @@ -114,6 +116,24 @@ class ChatViewProvider implements vscode.WebviewViewProvider { } } + public selectedCode(): string { + try { + const editor = vscode.window.activeTextEditor; + if (!editor) { + console.debug("Abandon: no open text editor."); + return ""; + } + const code = editor.document.getText(editor.selection); + const prefix = "```"; + const codePrefix = `${prefix}${editor.document.languageId}`; + const result = `${codePrefix}\n${code}\n${prefix}\n`; + return result ?? ""; + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return ""; + } + } + public clearChat() { if (this._view) { this._view.webview.postMessage({ From 1faab26623e1b01daec9e91c3ef7ad46bfe814b6 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 16:00:58 +0800 Subject: [PATCH 31/37] Add Apache License 2.0 headers --- examples/gemini/node/pipet-code-agent/media/main.js | 11 +++++++++++ examples/gemini/node/pipet-code-agent/src/chat.ts | 12 ++++++++++++ .../node/pipet-code-agent/src/getCommentprefixes.ts | 11 +++++++++++ .../gemini/node/pipet-code-agent/src/gitCommit.ts | 11 +++++++++++ 4 files changed, 45 insertions(+) diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 58e83d122..f89e0ed5e 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -1,5 +1,16 @@ /** * Copyright 2024 Jason + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ document.addEventListener("DOMContentLoaded", function () { diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index b1ce7d821..b7d097113 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -1,5 +1,17 @@ /** * Copyright 2024 Jason + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import * as vscode from "vscode"; diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index 16e0d6bca..42ea362ae 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -1,6 +1,17 @@ /** * Copyright 2024 Jason + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ /** diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index 54340b7e2..aab700d58 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -1,5 +1,16 @@ /** * Copyright 2024 Jason + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import * as vscode from "vscode"; From 6a793c1f9a9a76f5282e0849d5b54a1d9df30246 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Thu, 18 Apr 2024 16:05:00 +0800 Subject: [PATCH 32/37] Update license headers --- examples/gemini/node/pipet-code-agent/media/main.js | 2 +- examples/gemini/node/pipet-code-agent/src/chat.ts | 2 +- examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts | 2 +- examples/gemini/node/pipet-code-agent/src/gitCommit.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index f89e0ed5e..39253c34b 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -1,5 +1,5 @@ /** - * Copyright 2024 Jason + * Copyright 2024 Google LLC * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index b7d097113..367e696c7 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -1,5 +1,5 @@ /** - * Copyright 2024 Jason + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts index 42ea362ae..c5ac5d318 100644 --- a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -1,6 +1,6 @@ /** - * Copyright 2024 Jason + * Copyright 2024 Google LLC * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index aab700d58..0253a2f16 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -1,5 +1,5 @@ /** - * Copyright 2024 Jason + * Copyright 2024 Google LLC * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at From 05dc6b244cfbca3af2f50ad5bfc9d9a99bdc150c Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Fri, 19 Apr 2024 10:36:36 +0800 Subject: [PATCH 33/37] remove webview.html --- .../gemini/node/pipet-code-agent/webview.html | 143 ------------------ 1 file changed, 143 deletions(-) delete mode 100644 examples/gemini/node/pipet-code-agent/webview.html diff --git a/examples/gemini/node/pipet-code-agent/webview.html b/examples/gemini/node/pipet-code-agent/webview.html deleted file mode 100644 index f9888ebca..000000000 --- a/examples/gemini/node/pipet-code-agent/webview.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - Gemini Chat - - - -
-

Gemini Chat

-
-
-
- - -
- - - - From d12b3987972601bd9dae657850ef37b81be30221 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Fri, 26 Apr 2024 16:35:28 +0800 Subject: [PATCH 34/37] feat(pipet-code-agent): clear user selection after sending code --- .../gemini/node/pipet-code-agent/src/extension.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 6981efb81..113e4fae2 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -124,10 +124,18 @@ class ChatViewProvider implements vscode.WebviewViewProvider { return ""; } const code = editor.document.getText(editor.selection); + editor.selection = new vscode.Selection( + editor.selection.active, + editor.selection.active + ); const prefix = "```"; const codePrefix = `${prefix}${editor.document.languageId}`; - const result = `${codePrefix}\n${code}\n${prefix}\n`; - return result ?? ""; + if (code) { + const result = `${codePrefix}\n${code}\n${prefix}\n`; + return result; + } else { + return ""; + } } catch (error) { vscode.window.showErrorMessage(`${error}`); return ""; From 2b01ea0ab5707dfac5a8a043593526631d949be0 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Fri, 26 Apr 2024 19:22:19 +0800 Subject: [PATCH 35/37] feat(pipet-code-agent): Add text selection for chat prompt This commit introduces the ability to select text in the editor and have it automatically populate the chat prompt. This makes it easier to ask questions or get help with specific sections of code. The following changes were made: - Added a new function `onSelection` to the `main.js` file to handle text selection events. - Added an event listener to the `ChatViewProvider` class to listen for changes in the text editor selection. - When a selection is made, the selected text is sent to the webview as a message with the command `onSelection`. - The `onSelection` message is handled in the `main.js` file, which updates the placeholder text in the user input field with the selected text. - When the user sends a message, the selected code is prepended to the message text before it is sent to the chat session. --- .../node/pipet-code-agent/media/main.js | 8 +++++++ .../node/pipet-code-agent/src/extension.ts | 24 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 39253c34b..0ff3d9e7b 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -67,6 +67,11 @@ document.addEventListener("DOMContentLoaded", function () { } } + + function onSelection(text) { + userInput.placeholder = text; + } + function showUserMessage(text) { const messageContainer = document.createElement("div"); if (messageContainer) { @@ -128,6 +133,9 @@ document.addEventListener("DOMContentLoaded", function () { case "clearChat": dialog.innerHTML = "
"; break; + case "onSelection": + onSelection(message.text); + break; default: console.log("Unknown command: " + message.command); } diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 113e4fae2..a1f0f98cb 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -20,6 +20,7 @@ import { generateReview } from "./review"; import { startchat } from "./chat"; import { ChatSession } from "@google/generative-ai"; import { generateGitCommit } from "./gitCommit"; +import * as path from "path"; export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand( @@ -73,17 +74,35 @@ class ChatViewProvider implements vscode.WebviewViewProvider { webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + vscode.window.onDidChangeTextEditorSelection((event) => { + const editor = event.textEditor; + if (!editor.selection.isEmpty) { + const startLine = editor.selection.start.line + 1; + const endLine = editor.selection.end.line + 1; + const name = path.basename(editor.document.fileName); + webviewView.webview.postMessage({ + command: "onSelection", + text: `${name}:${startLine}-${endLine}`, + }); + } else { + webviewView.webview.postMessage({ + command: "onSelection", + text: "Ask anything here", + }); + } + }); + try { chat = startchat(); webviewView.webview.onDidReceiveMessage(async (message) => { if (message.command === "sendMessage" && message.text) { + const code = this.selectedCode(); webviewView.webview.postMessage({ command: "Message", - text: message.text, + text: `${code}${message.text}`, }); if (chat) { try { - const code = this.selectedCode(); const prompt = `${code}${message.text}`; const result = await chat.sendMessageStream(prompt); let chunktext = ""; @@ -123,6 +142,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { console.debug("Abandon: no open text editor."); return ""; } + const code = editor.document.getText(editor.selection); editor.selection = new vscode.Selection( editor.selection.active, From b84540de92e116fe1d5691f5001a2beccd9defb7 Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Sat, 27 Apr 2024 13:51:01 +0800 Subject: [PATCH 36/37] feat(pipet-code-agent): Improve UI and user experience This commit enhances the user interface and overall experience of the Pipet Code Agent extension by addressing several key areas: - **Streamlined UI:** Removed unnecessary padding and adjusted element styling for a cleaner and more modern look. - **Dynamic Textarea:** The user input textarea now automatically adjusts its height to accommodate the content, providing a more intuitive typing experience. - **Improved Scrolling:** The chat window now smoothly scrolls to keep the latest messages in view, ensuring users don't miss any important information. - **Chat History Persistence:** Chat history is now preserved between sessions, allowing users to easily refer back to previous conversations. - **Code Selection Integration:** The extension now seamlessly integrates with code selection, automatically populating the chat prompt with the selected code for a more efficient workflow. --- .../node/pipet-code-agent/media/main.css | 11 +-- .../node/pipet-code-agent/media/main.js | 36 +++++++-- .../gemini/node/pipet-code-agent/src/chat.ts | 9 ++- .../node/pipet-code-agent/src/extension.ts | 77 +++++++++++-------- 4 files changed, 90 insertions(+), 43 deletions(-) diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css index a9bb0577b..9e5b5bbf8 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.css +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -8,17 +8,13 @@ body { display: flex; flex-direction: column; min-height: 100vh; - } main { flex: 1; - - padding: 20px; } #dialog { - overflow-y: auto; border-radius: 5px; padding: 10px; margin-bottom: 10px; @@ -28,10 +24,15 @@ footer { display: flex; align-items: center; padding: 10px; + /* position: fixed; + bottom: 0; + width: -webkit-fill-available; */ } #userInput { height: auto; + line-height: 1; + max-height: 300px; resize: none; background-color: var(--vscode-input-background); color: var(--vscode-input-foreground); @@ -50,7 +51,7 @@ footer { border-radius: 3px; } -#sendMessage:hover{ +#sendMessage:hover { background-color: var(--vscode-input-foreground); color: var(--vscode-input-background); } diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js index 0ff3d9e7b..fc6015dfc 100644 --- a/examples/gemini/node/pipet-code-agent/media/main.js +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -23,6 +23,7 @@ document.addEventListener("DOMContentLoaded", function () { const sendMessageButton = document.getElementById("sendMessage"); let chatID; dialog.innerHTML = oldHtml; + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); const allCodeButtons = Array.from( dialog.getElementsByClassName("code-buttons-container") @@ -37,6 +38,30 @@ document.addEventListener("DOMContentLoaded", function () { }); } + const allChats = Array.from( + dialog.getElementsByClassName("message-container") + ); + if (allChats) { + allChats.forEach((chat) => { + switch (chat.firstChild.textContent) { + case "You: ": + vscode.postMessage({ + command: "updateHistory", + role: "user", + text: chat.textContent.replace("You:", "").trim(), + }); + break; + case "Gemini: ": + vscode.postMessage({ + command: "updateHistory", + role: "model", + text: chat.textContent.replace("model:", "").trim(), + }); + break; + } + }); + } + function adjustTextareaHeight() { userInput.style.height = "auto"; userInput.style.height = userInput.scrollHeight + "px"; @@ -53,6 +78,7 @@ document.addEventListener("DOMContentLoaded", function () { messageContainer.innerHTML = "

Gemini: " + marked.parse(text) + "

"; dialog.appendChild(messageContainer); + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); } } @@ -64,10 +90,10 @@ document.addEventListener("DOMContentLoaded", function () { +"

"; addBottons(messageContainer); sendMessageButton.innerHTML = "Chat"; + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); } } - function onSelection(text) { userInput.placeholder = text; } @@ -80,6 +106,7 @@ document.addEventListener("DOMContentLoaded", function () { "

You: " + marked.parse(text) + "

"; addBottons(messageContainer); dialog.appendChild(messageContainer); + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); } } /** @@ -101,7 +128,6 @@ document.addEventListener("DOMContentLoaded", function () { } function handleKeyDown(event) { - console.log("enter"); if (event.keyCode === 13) { if (!event.shiftKey) { event.preventDefault(); @@ -111,7 +137,7 @@ document.addEventListener("DOMContentLoaded", function () { } sendMessageButton.addEventListener("click", () => { - this.chatID = Math.random().toString(); + chatID = Math.random().toString().substring(2); const userMessage = userInput.value; vscode.postMessage({ command: "sendMessage", text: userMessage }); userInput.value = ""; @@ -124,11 +150,11 @@ document.addEventListener("DOMContentLoaded", function () { const message = event.data; switch (message.command) { case "receiveMessage": - updateAIMessage(message.text, this.chatID); + updateAIMessage(message.text, chatID); break; case "Message": showUserMessage(message.text); - showAIMessage("Thinking ...", this.chatID); + showAIMessage("Thinking ...", chatID); break; case "clearChat": dialog.innerHTML = "
"; diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts index 367e696c7..e5e052d2d 100644 --- a/examples/gemini/node/pipet-code-agent/src/chat.ts +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -15,9 +15,13 @@ */ import * as vscode from "vscode"; -import { ChatSession, GoogleGenerativeAI } from "@google/generative-ai"; +import { + ChatSession, + Content, + GoogleGenerativeAI, +} from "@google/generative-ai"; -export function startchat(): ChatSession | void { +export function startchat(history: Content[]): ChatSession | void { const modelName = vscode.workspace .getConfiguration() .get("google.gemini.textModel", "default"); @@ -52,6 +56,7 @@ export function startchat(): ChatSession | void { }, ], }, + history, }); return chat; } \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index a1f0f98cb..9d1b3f172 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -18,7 +18,7 @@ import * as vscode from "vscode"; import { generateComment } from "./comments"; import { generateReview } from "./review"; import { startchat } from "./chat"; -import { ChatSession } from "@google/generative-ai"; +import { ChatSession, Content } from "@google/generative-ai"; import { generateGitCommit } from "./gitCommit"; import * as path from "path"; @@ -52,9 +52,9 @@ export function activate(context: vscode.ExtensionContext) { } let chat: void | ChatSession; +let history = [] as Content[]; class ChatViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = "pipet-code-agent.chatView"; - private _view?: vscode.WebviewView; constructor(private readonly _extensionUri: vscode.Uri) {} @@ -93,37 +93,44 @@ class ChatViewProvider implements vscode.WebviewViewProvider { }); try { - chat = startchat(); + chat = startchat(history); webviewView.webview.onDidReceiveMessage(async (message) => { - if (message.command === "sendMessage" && message.text) { - const code = this.selectedCode(); - webviewView.webview.postMessage({ - command: "Message", - text: `${code}${message.text}`, - }); - if (chat) { - try { - const prompt = `${code}${message.text}`; - const result = await chat.sendMessageStream(prompt); - let chunktext = ""; - for await (const chunk of result.stream) { - chunktext += chunk.text(); + if (message) { + switch (message.command) { + case "sendMessage": + const code = this.selectedCode(); + webviewView.webview.postMessage({ + command: "Message", + text: `${code}${message.text}`, + }); + if (chat) { + try { + const prompt = `${code}${message.text}`; + const result = await chat.sendMessageStream(prompt); + let chunktext = ""; + for await (const chunk of result.stream) { + chunktext += chunk.text(); + webviewView.webview.postMessage({ + command: "receiveMessage", + text: chunktext, + }); + } + } catch (error) { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } + } else { webviewView.webview.postMessage({ command: "receiveMessage", - text: chunktext, + text: "Chat Start Failed.", }); } - } catch (error) { - webviewView.webview.postMessage({ - command: "receiveMessage", - text: `${error}`, - }); - } - } else { - webviewView.webview.postMessage({ - command: "receiveMessage", - text: "Chat Start Failed.", - }); + break; + case "updateHistory": + this.updateHistory(message.role, message.text); + break; } } }); @@ -135,6 +142,13 @@ class ChatViewProvider implements vscode.WebviewViewProvider { } } + public updateHistory(role: string, part: string) { + history.push({ + role: role, + parts: [{ text: part }], + }); + } + public selectedCode(): string { try { const editor = vscode.window.activeTextEditor; @@ -168,7 +182,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider { command: "clearChat", }); try { - chat = startchat(); + history = []; + chat = startchat(history); } catch (error) { this._view.webview.postMessage({ command: "receiveMessage", @@ -207,8 +222,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider {
- - + +
From ff9b70c792b0439b1fae4dded93de3741587392a Mon Sep 17 00:00:00 2001 From: Jason <5120011@qq.com> Date: Sat, 27 Apr 2024 19:57:38 +0800 Subject: [PATCH 37/37] feat(pipet-code-agent): Add generate code functionality Add keyboard shortcuts and commands to generate code using Pipet. --- .../gemini/node/pipet-code-agent/package.json | 16 +++ .../node/pipet-code-agent/src/extension.ts | 5 + .../node/pipet-code-agent/src/generateCode.ts | 121 ++++++++++++++++++ .../node/pipet-code-agent/src/gitCommit.ts | 1 - 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 examples/gemini/node/pipet-code-agent/src/generateCode.ts diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index f037342a2..01d33e9f5 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -13,6 +13,14 @@ "activationEvents": [], "main": "./out/extension.js", "contributes": { + "keybindings": [ + { + "command": "pipet-code-agent.generateCode", + "key": "ctrl+enter", + "mac": "cmd+enter", + "when": "editorTextFocus" + } + ], "views": { "chatView": [ { @@ -32,6 +40,10 @@ ] }, "commands": [ + { + "command": "pipet-code-agent.generateCode", + "title": "Pipet: Generate Code." + }, { "command": "pipet-code-agent.commentCode", "title": "Pipet: Add a comment to selected code." @@ -66,6 +78,10 @@ { "command": "pipet-code-agent.reviewCode", "group": "PipetCodeAgent" + }, + { + "command": "pipet-code-agent.generateCode", + "group": "PipetCodeAgent" } ], "webview/context": [ diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 9d1b3f172..545b64640 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -20,6 +20,7 @@ import { generateReview } from "./review"; import { startchat } from "./chat"; import { ChatSession, Content } from "@google/generative-ai"; import { generateGitCommit } from "./gitCommit"; +import { performAction } from "./generateCode"; import * as path from "path"; export function activate(context: vscode.ExtensionContext) { @@ -27,6 +28,10 @@ export function activate(context: vscode.ExtensionContext) { "pipet-code-agent.commentCode", generateComment ); + vscode.commands.registerCommand( + "pipet-code-agent.generateCode", + performAction + ); vscode.commands.registerCommand( "pipet-code-agent.reviewCode", generateReview diff --git a/examples/gemini/node/pipet-code-agent/src/generateCode.ts b/examples/gemini/node/pipet-code-agent/src/generateCode.ts new file mode 100644 index 000000000..e336a2e23 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/generateCode.ts @@ -0,0 +1,121 @@ +/** + * Copyright 2024 Google LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const SYSTEMINSTRUCTION = `Code Enhancement:\n目的: 将用户提供的注释替换为实际可用的代码,并在不重写整个函数/类/等的情况下,仅修改指定范围内的代码。\n输入:\nfileTextWithLineNumbers:包含行号的项目文件文本。\nlineNumber:用户想要修改的代码行号。\ncurrentLineText:当前行包含的注释文本。\nlanguage:项目使用的编程语言。\n输出:\n将 currentLineText 替换为实际代码的修改后的代码片段,不包含任何额外的行,也不要包裹在代码块中.\n示例:\n用户输入:\nlineNumber: 2\ncurrentLineText: TODO: Implement add_numbers function \nlanguage: python\n系统输出:\nreturn x + y\n注意: 输出仅包含替换后的代码,不包含任何额外的行,也不要包裹在代码块中(i.e., \`\`\`).`; + +function addLineNumbers(text: string): string { + const lines = text.split("\n"); + const numberedLines = lines.map((line, index) => `${index + 1}: ${line}`); + return numberedLines.join("\n"); +} + +export function performAction() { + // Get the active text editor + const editor = vscode.window.activeTextEditor; + if (editor) { + // Show loading notification + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Generating AI code...", + cancellable: false, + }, + async (progress) => { + try { + progress.report({ increment: 0 }); + + const language = editor.document.languageId; + const fileTextWithLineNumbers = addLineNumbers( + editor.document.getText() + ); + const currentLineText = editor.document + .lineAt(editor.selection.active.line) + .text.trim(); + const lineNumber = editor.selection.active.line + 1; + + // Generate the code + const generatedCode = await generateCode( + fileTextWithLineNumbers, + lineNumber, + currentLineText, + language + ); + + // Insert the generated code at the current cursor position + editor.insertSnippet( + new vscode.SnippetString("\n" + generatedCode), + editor.selection.active + ); + vscode.window.showInformationMessage("AI Code Generated!"); + } catch (error: any) { + vscode.window.showErrorMessage( + "Error generating code: " + error?.message + ); + } + } + ); + } +} + +async function generateCode( + fileTextWithLineNumbers: string, + lineNumber: number, + currentLineText: string, + language: string +): Promise { + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); + + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return ""; + } + + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + + const prompt = `fileTextWithLineNumbers:${fileTextWithLineNumbers} + lineNumber:${lineNumber} + currentLineText:${currentLineText} + language:${language}`; + + const result = await model.generateContent({ + systemInstruction: { role: "system", parts: [{ text: SYSTEMINSTRUCTION }] }, + contents: [ + { + role: "user", + parts: [ + { text: prompt }, + { text: "请不要将生成的代码包裹在代码块中。" }, + ], + }, + ], + }); + const response = result.response; + const text = response.text(); + return text; +} diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts index 0253a2f16..439573842 100644 --- a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -15,7 +15,6 @@ import * as vscode from "vscode"; import { GoogleGenerativeAI } from "@google/generative-ai"; -import { promises } from "dns"; const SYSTEMINSTRUCTION = "Generate Git Commit by Git Diff and last commit, Only return commit message.";