From e1c7a25b87aea74d24e69a6ecef0487b4838c177 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Tue, 13 Aug 2024 16:51:52 +0800 Subject: [PATCH] feat: add gemini provider --- package.json | 2 + .../src/assets/images/models/embedding.png | Bin 0 -> 6287 bytes .../src/assets/images/models/gemini.png | Bin 0 -> 19259 bytes .../src/assets/images/models/palm.svg | 67 ++++++ .../src/assets/images/providers/gemini.png | Bin 0 -> 19259 bytes src/renderer/src/config/models.ts | 16 ++ src/renderer/src/config/provider.ts | 24 ++- src/renderer/src/i18n/index.ts | 2 + src/renderer/src/services/ProviderSDK.ts | 190 ++++++++++++++---- src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/llm.ts | 9 + src/renderer/src/store/migrate.ts | 20 ++ yarn.lock | 37 ++++ 13 files changed, 333 insertions(+), 36 deletions(-) create mode 100644 src/renderer/src/assets/images/models/embedding.png create mode 100644 src/renderer/src/assets/images/models/gemini.png create mode 100644 src/renderer/src/assets/images/models/palm.svg create mode 100644 src/renderer/src/assets/images/providers/gemini.png diff --git a/package.json b/package.json index c3430d2d..5d444bb5 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@electron-toolkit/eslint-config-prettier": "^2.0.0", "@electron-toolkit/eslint-config-ts": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1", + "@google/generative-ai": "^0.16.0", "@hello-pangea/dnd": "^16.6.0", "@kangfenmao/keyv-storage": "^0.1.0", "@reduxjs/toolkit": "^2.2.5", @@ -47,6 +48,7 @@ "@vitejs/plugin-react": "^4.2.1", "ahooks": "^3.8.0", "antd": "^5.18.3", + "axios": "^1.7.3", "browser-image-compression": "^2.0.2", "dayjs": "^1.11.11", "dotenv-cli": "^7.4.2", diff --git a/src/renderer/src/assets/images/models/embedding.png b/src/renderer/src/assets/images/models/embedding.png new file mode 100644 index 0000000000000000000000000000000000000000..00e4903636b641ab3652ad45982e7cf5160a6173 GIT binary patch literal 6287 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 IU^?*IS* literal 0 HcmV?d00001 diff --git a/src/renderer/src/assets/images/models/gemini.png b/src/renderer/src/assets/images/models/gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..05bce50733711f2eee1c7e2d185df98cd968b1ec GIT binary patch literal 19259 zcmXtAbyU>P*ZwTsEG6AYs-(0mDWEix0t?b8(n#-864D`EQX)!83bKIGjWp6J-Mzc- z^8KCn56*HpJ2Q7??mV}iOO%e5Dk%{I5dZ+B>T1e*006@L3IYi5Fdt^_pY1Rogsy5P z9sof0`|bynDnP~v0BnG|vce0W?7hIiDl=8D&4be%U+pAYg*F1JS`p(Kia0mJm(VnFO9 zZi&?W{3*^PL6a9*O%49&ExGy9n{!$9Nq^CU!PClTXJQxM4|4wf*<5`$1R~;Qa6#Bz zgcq_XwQuD=eF6^|UA}*CwPB(vGqI!kBrasMfB;U|Ma3%XCl3Gqzdwj>gQ#Q31g;02 zNd3k?ck2pCQjTeU*)A^j>Z#e-!gD|2(K$-QElJS!D+)$!tSsA^!@37 zGo}N{u^uVnKE3{}CSh%1pD-&tRn5v9k3KBIC#z&VGbVZ($^mfGd8yDkf&C1;+CO*e z2{BNEgbJoxJQh%I?`w$U56=qUv54DZA=-$O|1fG@WvT!Gm(a{=U0wrYBrx&GMuFo zy^qJNP5mdfm7q!vP-;tt`FvK!;s)P+o5ZS+UFND6h$#6nO#lC11;1RZey6GFgqx8M z8t@*MDr7NgKh(u+c_qf;I|V>Z0tn!g>}+IkGQVh7(@6^E21k^BvFD=`qwWCe^cbH? z7KKn0H~>_UZ>@8`P;evI^L1-iqIb?^y~2L+(}6Hj%b)U}G2vp@+=HXbqjI18(4~pN z!iYn+a3}*cuZ%JTyw}sxzm5NSDqQlv`}#%ya|5>w=|Z5>l`pb!#mQvaPH=Z71(oA} z^ltJBq9#*T*pkCB`y^quew4TjL6zq`twMFH=1P32rXgX)Ob)JZwLZwq)nhcqw!CLq zxV!nDK=jW%ly04l`27dWK!Q|pM3OX$K}87uhYk~)=j1iR$BkIZ5bI}xGHXzjt z>l3uBZWvYQpDPr7^uKxU<&xucvzDX!ra#Lo;b^w$@5@|%N{p?t{LG7Gspz^lhd9>2 zW<=ZbT2BPGa;Wv^8;uT=d%VFdRH6W=LLy3FyQcsM!znoeFV=)NCcxgpvH}D8Sw!_CneigM3hPka)wSmtBf%~3RveSZMD_32B`>Rcio)^(DS>zsi5kbYNU@Cc;k1N z84;Jd+a|0^0a(XAQlUOXCwG#LN73DZ?J_fE*&pw zAaTFqwOPqK(jB^eWC^3C6}4=^=*i{kgyUBHXhJba;uVBA@;%&i)Z{*c&;n*dG1a!+ zIccZ-b-_GdarpVk!!6fF`%Jc{JG4PXxP^)oz$!`Tlwntq6nf)n3VSlCqYoPe{vtOu4@ry^V>bd7{5bGyZkz1tM?fI&hf zKDX`Gl^kp!7ik#-XB5Q10Cr}pJfZ?#{K=tQCcI(?=+Z;N?wfgz4KcEB<@ zjU+&ldn+zW2ki&6=WAAnp1&764PnQ;XyMBMz z9+~-kereu%$SFuCj+s*FPK;D^o*%OKL{JaODayuV-$22jQu$!0?Fe)0*6;6LCdbmC zJEMwLt1QVI;RG=#JX^Giy(2!&YcW%%!SdhTg1@9hyBG3B|Ckw~)$0O}g<%&ayP_O6 z#sYrUR(Nr=n3XsiM;5cU5#F|lPkZ~{hd!g_4;fEWrFRBakw$|DpNXGFVGfHR9^$iW z@VKmwh-U-*W5jdPd5}oB63;-8vo(XJaR{ zZ@w6VvWei;ZIY&+K;2g*piM&BIzn$Shs!-(A9&$1UNkALOz|Q92K5;G3);s$!4glz zsra@}aMW?2c>_&I^fVYF0M7T+ADJ(Ms;P9kP_}z8LT=!(vJSU61;2HRl92QVSCk8C z$Ou{u9@Z5y!%U6mGgs$tVH5c%K^)S3Zo2@PLZvxG%2(ZQ&y4va^o|S=d*+;Xu*pj& zhdBZ4apmU5jl7UxLy=nhPrC@}R9Z5tEcOtJWVvDz?6%Rjb=3}_=NOHD;P&R{+k@9) zqqqyps=?<1kAd9Zj%QHpV?ucx3kWyxmxU&qVN%JvT8faI?`-(M^@{^GpR?$(wIXeT&<3^0-g~ z1quljv_&UgGCIFQVc`nk^w4?Mns`*bbbb=6%16f@4H$p&=?GPI0bK9Vm!muggeI`w z%%d;OL=NNHvpo_THm`9Hf8)Q&tZwd>k=UC>II*ylTlU-uwsP&&JH}exkIeYDpZ8rf&Ikq_ zUL4#qo6J+L00^Le&|Y&UKCZ;VxT;i!YlV2G%dtil&GxJRAm;5t7#yJdMKiVUiwl1q ze9JYdoaiB#cKw=QOF&V{3X#|IwzzbS>8ZK8VBuT|bq|1cnW%ZGwK_`l6wU#B1ZU?U z7pnB;_C(GrWlI-lFT*5nyX_QIU9sVoQ=(2*=&9)ZT2gZFS`hte^7|h1?hIzymUDCH zhv-D=2XCoCRbhu=`0)!PymjIY+V!ZFUMQg`8}*G4#RXKc3q{xtX;V>ooE5WYmmHU7-EJ@F|)I)j;Q zE#5u4SiMIl;P~4q)x0CrRp50PjF4oCoruXuRXgw z_A6C=y<2SV*$oe*Qe7Ue$H+BJh|cG*C|xP?LRpD0azbY3$9|&)FQN6Y^<< z)|^O_7Kbg9^DC)S#9@9%Hwgc8$CmdYxXPHVP`_NLzUch<%)D&g-ojk!EUgStbL~a8#an|ge{@yb5SWwzFEE{Q$9{wT=9)lIrFZtpMS+c*N50xv$DPOw9{wn`D zOmq~!b}8QKI@Pe?cI=XGX?%A)pGwD}63X_D)eqNE&kqpPI0hxng7o{hJ50U~_5{yp zaz`ci@(aI#ExQU74hgG*mSo6?+Y7De!|OP}RVOPKunT+b^(WL1WHCb7+zC}j)cB$R zx$b(k|7J%pu?)X<4=sjeS=rsak%N``;o?F-;fdWhwlc;Zn(+0yAJSr+js!lZqYH~4 zN0wGF%JzZarT1T7Mx2Qi+T->{{$G=3I`12>Z3Z$KPUB0@l883$z>d-kv3KrK>J%sd^L~5*;0z;*&YxMa z<-i=b1W)mL>To|i59+VVN92B-5|^We$0)2bg$!VwlF3sY6V*~N)#+7CvOT(Lg4`Mn zzviWTxIsWbLqdG_82ok%R-v3rSY?La@!F^i#8C;SP;PY5Hbj-cX~F`z9@zN6c-sEL zV-La-p-6y@6-T~A6uPzLH#xP`9Xw(`5*M#Se{!r14QyA{J~O8o5RAsFA~;Nrh+EWa z@_}#~pz&u=D4*&T5xu%Ruy(-ftcJ>7fdEzfAvlE;kUEKyT<9)P*yDzAYT&UNdZM$t zjwK+-QgnUL5(egRRe%^eBdAAr9bf^9{KO<*gqlN~pX5(KXi=Q|lrcJ+@%=$_NN43% zTw6{69*Z9K{_=U8ZYff6Kvlj+WG#xNi-s~fMpSi3dKUdcotfofG@+2dH){zF%mQxW|1pAjdRu zm53KA7G|kKRiJD2?MOvM09RzALY4IZvNOp{d#1HKK{G&w2}d~I%(!h9$EWWtc0lD- zOP|5AklpA_gQ+Qy{9Jo!I3%B$K63BqG*<+NPgP~#N9!ha6pg$5l7lg^6**A347){yO3B6L1Do7qjXL9hSx6SICc2Y6^OcuTLjZ z@4WbP7RbYL{Ya6V`*WWpmsf7Idj|Dyja4j)J}!Kd-5M?bRm$}?uiY2@bw4@k1mJhp z`7pMoK~*uotp&eePl(h5hE(&hb9`04EgoY`tygC@xxUZ==1E4nl2r~fc9^ogtuCf` z;UI4~8$5lF>8;0ol$u&i-0roEQMV=baf;QqjE%-<5j4)^rb9U?K)s!i9vM)PZ)#siNm&!&tP-HKR~ziv3@AySYMv>IIq zf#?1p_6OZl_KZ0yzg`p~f7*yx2Cee}Nq z>pUa8*4J@sSE{uy!+Hhz8sWxunzB);y(2JY*1XlDnA+2{zf<4>C?6weq^tOZh^I&t zq^PU=k01s&lZ2ddHj|W?JkOf#y!!A94RVlp!T;ieNM)@h3&x~lh43Iij}irEFAYBO z(CG&(%XM=r5_$iAJk+0yEpe_^*(TueLz`W4_v^gNZS>yr*qZ>D+mm0ucx%z&I0&WL z+z0EA)$}M`^to%Lvc8S;M1zfuG3HwS?!3Qv*u@3q!k@#9i|yhi30dX)upGRuG~eu* zt!m>{cKlmg^d=^*(n#XC(*f2`~|1ELCB6;kqU@!nv_Rx)8fJQ& z)t&++RamAGZyvtydIfUVIQJ7~9Bm+_oe7l-oQ{wPPZ0j=D%Qf8m2ZP){$~i&X9^__ zUBSt=e>2zW?^Pf3i$*geVgK+Ege`(TPMNip`U{WUO!94yKoo?Qo*`6!89am``k%V1) zh+yNrG#s`W!>{gMK+_c436`;kB`X|(*uU|-NbzNj7VtT!vSxjCpS}N>ErYNEfT5}g6y=Yfwddsokd{Sq4G{sdpmuJQ0LMo__x*U5)hqr)0gb9f+s z!eWyNK;bphYC-JInR?$!scir^4n$-nC& zQVJKCB9B~5o^o%2uxa@aO&zWNEHx^l4O*@7T2yK0!}wzn!FRER-xziTWO8CdkJPDb zWdnywmm}@EKLwg=<9I2kiaIK5==G+zRhxjFqdfjDbgw7`rG6~#`jm;bU~AlACun?H z)&ol4AQ9^o-y5eCS54RY$~KtO_#V`Gk!v#J5_EIoGN<$TS7{^%phfYYw&=&AFe$Bd z=h$eY>v7-B_~(W1KhB`5#tXM=tK0@H+vN6pt~ua9czE$ zWki~<>i1|_l-mh0NT_ak2e&&x0XAtOjg;_)Kf(1pt(vFRFm1at?L8PDPSLd>?DXL` zC9`Bo#)a~cihb4yu8b&SnNM+sYdm$ez53dLnqhHN&)jO+Y{-rqjq5h6-Mg&#wcxl)8L;9GV64PO_Nk&46^*HIEwvFLr%gcHyK5185K4Nu$ips5Vm zOVzA9-;564(14GF(nUdIsd8QmN8WM^aK*3Y1jKuWv)~JfifbL6b^V$|ieZjA5T9#0 zDBYU**|TbD*IXC2+`_##Zz5DxV86@v|GF>S=m=plgag5^7lXQA^IM~0NDQY9jrwFZ zQeJ}Sg##PD*?IhhR{Us*Pt3YCyB6T_ESeQeJxitl+|Ay=7~LMSO){`m+%F`oQI8DB zO{sW*1Qb>i;@X6+On8qjkunmW-ot&2M6UQK+0N6o-_`jd7ar~$s3;Jt05(;QQz!%4 zg?C$m0@gt^1i5dz*oG0cX#L~(`Hv?QzYE%3pIod?b;$t5cnQ*k#X`rco&xSNBH-5~ zu|y=Y54#vDsK$fru-#ALmblj2z$tTR(nMY!Wc}7(B*_sWlN_U(5#W|$Y^)L=u%z34 z^HQ#1ub0*3XAvPLh$4K{j2hZ}vY~bm+LvBebNHS++7J~d%;7Aaoree0Qf>Ohv%WgObqw_JjjJB3I0%}({DLOoBa!X32X5lKg{Riih9q0l zNJRS#{#Mo!J*vG&^)>M_G8nwWG!J4T3P8OlheM=+qG6w8w zaF5gqyy-E(rg1r~3Vwa>ST=v})_M-Q@CVH*R!YdmQ{<^!Bhgxsa&dcdl;oWD@~XI= ztqzEzQxk>j3Pjh3hp`VSlN6{fKG#v!Ci!hsy0rVXR-uxp_7$303XPp*k?O&54Hj5# zi~ccF{jT4awIl2m2X5O&&P~bC&FFH2C*${II8D=E-z!)yAVoQ3wu{K#BPTIL@6}79 zSqZ2lW07-#&P1+jI?6Xpi}|9fl4}!2erKewcyd};{{*cgi*i^x_xQx!d~OH$mPUIH z;Dv%U31#;VvNfsM2-N9u6Ut%Z{N{fP=e7hr8GqUyOrGq&{h1)#{hJWpUx7IBjh7Mg z7d2aun~U`-`J?58#y-?)v+VEs@`Tj|-NgWxeXlF9jwA}G@Eq@D_0a;>reVvMzoSZ5 zj;)#AuGU&`|HaXQ-JUG<&L+6es>?Wm#X`?z5-!${jy!tZSAc+?$6g_Qo6D0n$MZcG zZ3p~$7Mh8c@9f$&cUYuDSn^gPB^0A{=wi8QLutnUqp@u_j048uSD zfQ{Kxc5$SKcF^}FBQ~VNsO@R_u}`zrIqE*hOOYMCUu;GP@hg@JTTl@UGZABp<2tqP z91deik4#*5+;d^H$0y8@Cet48i|@_Em4PimGZWXjal#S-tAjfz z@-8}S0YDs+bK4GKWtsJRS~*h?V*>hJf8YOC-DSIQ5guFl_{JNw!e3ZZPB=)3(yFFI zsnbf*Un}6?#o4AmL#?y5_Tu`H+8)Ph8*#1WJGqdwT;kwo6M3(3XE`|BE?C87)|wMF zwTsE&-K=BzbIWR1WGmEGVL|Wr<&b?(1>EBd|K%;;97zhWi0_U5kXCKxO`yje`O6TFWD2h1;ckF@ zrF%%<0qMn%lz8!Zau81qgNN30>zHyjI!4fcxX#ET*@EPWJ6klS_xM<1oTFiIlQNv^ z0rVNnzWshv#E|>k5qHT*2=?T#Ntjfe7He%n)30`2E`JYsAs4QpVWt&i60YiwF03jr z&QKD!;AQ!w-kN+luU_yO^P;V@f=UkT!551aD|?wC@?d_o`lEMLgcL__rBLK;;kc`& zF}H!jK6&G0{MI8zVW~*JuQ=^Vw7^{bfkmKQ<;;TfX_nFH84-8Jzy!5uK-|R%^dFEy zDvckGHf^~DWx)Pk3_}HvwE9`!W!@~V43Qgun>7i33n6A|km>eK;MTs?dgUCohfHxA zIEZ}=WMG*Q{|Uw=UgB<~3#C}pfA;XBmdSnY66*8gz#=YztDkD5gNwBCQ|1koYu{&t z`!D7u{Dw1rZkWIOX*&`Wdz+D@NO_irhD`P4y-(Oh z`8@8InjL$F0BX1u`k2)!84Wjt#2;m5aH4diS#U&RNHW}x(vrW*Ew(m@DpmheVf&t& z{9&=P2k#3ap!H*{A-IGg5L1QgA`bGr{PkQops@(&Kdcl-EfpA@3H%7Kt(@thYJk@9 zxJzH@Q(1zqA|U+}oKIK)n`P~%*?aeb63UPn?@M>jl5TF${X4`3i7^sS1etj%J$LDL zKJ*Gk$+GX#1)YvD^=NXb|4!_LWT47xWfw5T1NjdV>3ITV_QzhUjSz5lc&H>$L#N&? z#Z_!Aa!9EdoMOr+ro83A9Ni#YKpb{hIpFCzEK(SA)nlUSFClneaWo11KP2mB`uCGY zS+KlTs^5OI?3SObRuj%k6(8dK#NqfkDVxNVt(nX>{Pg}C{NpcvU#_tgehy|ZJ+9-c zbfyYDI6a&1uVWb4ysw5}(_+gP9 z7s93W?VL7cmTm4e#u};lc`VLXJ`1Z0$UBx;g~_N>(6u>{3v6!?cP&+lFTf6!KIA;F za=>jXF~2b~zhxDz#6G-E{b^VDw>udcqy?kqD#s1QvG^}EhiuWC zhCVkE4G#kn7u=Mub`fIr&X_E0;4N&S+s$5GEs+6!DlXb|pGH>d0ORm+{*lKWhFC79 z734p=X$(}9Ub%Uu$_dSDxOC<0v_edi)GXo}q118dVbI@GiZ83e-Z2vUIePbG!|3a* zx>$)#O>j+!wra%~5gvRvHq&Ev3|*#LU;h<^mpx@oe|xIR>e~YAe?^JMNejsStNWzc ze=}G);Pn6vTpP^ke~pQfZTKfur`IYjMH{AF-;Gu!EFD>8WOa-u& za%(OD`e?Ilnq+u)hc4P*ErON!rMmt{ElZPy+ZRF;TiAU|h4E6-GVWGFlpxnmFaO-@ ztwQ9c;OkS5($~n4WGNoK6H(GZ_ifnQkdy7?v7q%e`z}T!WK`JuLsxsA)XQwnk3O4I z@DZp?5$Vh^X07mjTcbhHXF<0uk)!%sb6gN^{|#a;t40!OuQ6jxM+uCOI*aelKGpZ?%Gil}UdI1twNd*r+id6><&Q7^elP1?#0hg zWUNmew&`zn!nhj`x{-z}?)*aYj|KY08OL)p>hkitK1>1eY~XY@gZrk?N8d$|LAU-x zk_VTf5^95vDP`iEh>PrP*cW5NohPZn=zyn{SL0XYEHmFnw0`4K07hmFn`Pzkr@8aa z1AnAvWh+JO_M(OvVm!|_8xdde^ZdHjo7^X=lLGf^p^i5Z)=TjqJlD&dXer(+)e6+H z>DXvzLFKt<*qNc0pYMSy!nI;xm4}T8-J4+k1>{PXpznG0#PE@<;r-zrQxzu`_0mr- zDl={>Tl}0&J0WX6Yv0v*$_9|bSH@$WdkQ%jAohWt_n?OtY(*rc+-$1wX<^AlOmX8` z7OX;G_T;lQvhq>bdsoxK1q^{?9K&X*akH)T^=lB`k)H;Zr2f=d2y3sN9F9Tj8isAr z{pdW64Jbd(nm3!qt=S)KrLb0GM?H}dwo13msRegw4vU9>{7YtFZ_@f|%uO{!7Hy}) z)U$|EUo5@gCLkAQTyg7qVg2@I&=d&G2?`K2!H0MAGRL?--e7req9DiwFM~dfx)?dW zoQZr)uN>H`zfQixo-U1QMMln`<^^sBbD%#~ZV#UP-N|vCzAFz% zd(bm;05!0*s`nS6;h8-So-45ZA2JfpargIKt2=Fi*WP4!`G&74Ge9khqV^{Kf7P4~t9vne{bVEdxF zX|_rYn(hJJ^i}uNraJ332Z=U8(Uya#zM}^%Xz`!XcB?A$&b%hxh1~Me=t02lY^ki! zR1?mfOK~+=N6!565BOo;-sieh;rKD-a+%q$fs*Ifit^IvY1HP#vNBnlLOtxqvC%9_ z5~R!^&rp)=wj}nqyL;y3)l1P>R&pX|j7R_I0K)9=S#hXe75%J=ME=ojdnaU$1v!n< zrt&goT=Jic7MgGnlrR1#@ z_Zq$a_^_LV_eVT3aC*_4Ggqm#xid0Oe9H+LNO}z-xJYgfUS;-L?6dH2^_>qWzy|a+ zJua{Xek{t&?#Wv1DRsx0D9FElyo}=1pP+hQH%6|A;9xz$520^MW_5TC?A~IJT-U4# zS9|CZ#42k_a80Pv6A_Isj#vL)nt;@OU#D|VUX(M+mZXwBRPa%Am`)vvQz;0ko(o%z z?|xWG*mi_i$D^*AQ?`pUjmx#ijC893%&AiE* zq}kNl+!(!){ifpkd>^8FUW*{5a{dyjV8b}ac998ySxw|Q*O7h&9a`J@s zpUl?#PyQ9S1a>bx7VtmIv@kF~ybSFg-tij8)10mv>0m!Y_3wQZ8BVv=>O z*9FNZ)&AGt^>wA$On_<#o0nb9!`yqenwSi!l4&C8lrDJEV+IYF_Uh`rOvzN9UFZG# z&XW?(9F7xEDzec^gWT9xJ;m4))?DO8aui$kd5^wozTs*1Ix_u-zjS*4RX4^(5j;Wt z@$e{sXh!}(^&qA6Vq-ctMUZ3Y2v&XB-0_<{5%ew^J0L?~Z5V{B(6;UIAwQ)>!Vo=k z)EU<{Eg%@C@t!iPKR&@6MqpKgUBD=`$fJo&J%>HorG3Zr@eFzV1txvPehxZ%cfX01 zYd6Su@QMhXLsG&ZigB#O;0HM~5P3F@9>W)w zaT8A_0C$M&J)q_K`L*D4v_d&7lO?Mk)vp4KWnvW33Nb%L=JpIPul?^Sv`NveF}{4S z&%d<#T1*xCWp~D@H##HQZO7#sTr5nyCnG}yixl^dd@gyBm0qGB3H#cMmf2FShjI-$ zO?0#m5{DqZaRHN8lvw()a`J6DCc#Qn0OcJFnx2eQ+gHks-K@unBQiOO25oO{_Qt(Y zAh(HGh5LwIuTUa^3>e>;#ME*BLY`@^P2;e%hqEJ9sTbN~jn%?U|166qXxmNuJJnV9 z_MFF6ibGRhwY+fRM;-m?JnPM)Cs%T^y-vdaLPLj@zXWZ!*t}KL>e-+yyv+T2u4+5? zLbhz(f9JzN|H9PM#LK_!7tu%=DQM>}5WVG((LAp7;heytiwolFjyzYAU#(VT|5?j; zh~&+V9$k+CVs4rNQ|MGoy4TlJeM86NboJP1RJ-ZZv6vhH#jbeKD2-y=6}q zy-}g6aw6OOiXRjKH8YPhW=0l_N3sK0s8pEPYhlyXUO%^6jQWBCs$=CMk!eB8p>I&* zH>`0{2dPB&q$aoUWxG$-vtfFix^V4!^$$KkK?44wz|^k(F)o*5Pw6qyFa!d*(ehch z2U+t@@^`F}TYq^{$+@O7z3D`|ns8v!6aZV8W}rp={^-EcwlraPGe zt7WWmOQIp?W@I!`+b>Fm2jHssVRuoCaX>8QdHrnHoo=2Fq~3B`H-~nJC9V=W)p`0R3F>P(%BcH;HtG>zpKrf~2N>7u<` z#T*G!K95KpeDNw<&RLe990uAdC5)F+M7#f%yGMq7Jz`JEtzq^15)P(Fl;SkI zeyx+7OwH{48e8tXXZO=LEUs!U9Qc3aJgbQ}f#yb8Q=~QM;wLgw+Xt)T)N#W!=$^oT zWk2%_QRz>Ak*3OKLXORTz>2xAs0UK{gaqGD~; ztSpIzcdqJZ)MoBj@M`Cq{fR%ZLBIMeklYx0mXbxFU&Zxa{$F(VUY=n9Xtq;i)@`Z& zI`8}zducAHA8a~w1Le6{Pxs~MMe2H)HwX;AI30daLCan2bMJQ%9X(b`f&4$3%J46{ zm3z6O2IvgE2RRvEK|Lpz3tS7J*H}_ z$?@sm(4J-I_{KFD+3t9z#0QW|e%pcO9!uwnQ(ADPlZR#yEZ~!2x1xgYu&V|<3MRWZ zJ}z_*WmT#hF@7?1kuaxs00A3%G9(Xh56rVxg?)|t=E+c>SJ6AuW*23;@=Kj$HFmn+=3#t{ zCBI^B6m&(y&{!bSXj$_E{S5ysZ^CqkilExBtb*KqBwRRrGnoZ;@IX}I`LbIbaZRKRQ`jm#4Qffa zh~}e55aE?M@W3qO=Ws6UJgUb@U!=QWH8b}`SHKca+=a_@vn}!`9d=AAVA--a`$W)m zw0zNkzSf=6=at%V^#G^X~|VhO>*M`@!EFwet{g%6zf<;a8cDu@~JujjvINKiUe~I z=Hx{0C$mierPN$rWpS;7EeXH1++pO5-y3c@>j$`ib+-Z3SEYE+KX{1~cr{!-&oU%M zu_!~tXxjs+k$YlaSR4iMhRPb!i}(AYuUxi~_}R$<8IrOrc*J1NitbfQ=R)^#ujl%T zvV5Pa*)Ju=Hb<1Voc4&}Rjj`(@-#mRz5grf|6O$*A+xaYD>o`L&z&=1T3i>s&g01K zedhD+lCR4Y047!MsMSHqBnzZVUp-YIsfVm1^=N%VlSr!WRG7yX7gMAJ55Vz_LkX!V z16B#b`_waZKCB^p0Cl=2j4Pv+jQgr?IzB5zFu1!9D?CrNo?~;<=>+uZL`e%(`xH}! zf7!~DM-Df(?CoV$3Ha^Owm_a<^Z3@qcv~D5C3KEa-v25@w}*!lLUu20tFR)Q7RNTQ zXSZQP-v_#Tku8cC+`e5Mk^+ipA)82v-m6fDKDF_~Mt=7#uWrlDyqLQ0|JpCK7ei0K z_U|geVq70Q!?%o!NR-p=lj#XZ6%7{^wAVup(l+Fsuw1#q3ZL}vY*ZNhymnq4L)|E+ z(}CLViY1#vh7*IB=bh*&QtWApaw}!8Qy7QJ(GrBEv~_tnm5FZ|>_vvYFb|$)u|Kp) z+EjCL68O!_EezX;`gn4QE&N)jYIVdyc)re=t4(X{MSqnwStdESmXdxpdC1f^dmzyv ziF#9vi0y>Ur{no{rQq~-1^e_oo6Aj@e2HL zY7l>Nn5oIHdd!QKl1cyoc36#NJa%*7Gf%7R6ozuOo**43pg9giO{`|y6Lv*^Ve**- z><=V(NxD~8CMq_gVeiMGWx8QB+LZ85YjiB4o$CX7Pnop?CS|$ebMx+>pLYwurdjsc zkz9Ppv?v3!llTv?B4(J3>D+|eQG9K78qR|QiFXfOPHpdsqoIVI${0I!Hun~C;n47- zcrN|94d3go{Gz0kRBj`86DmR4n+Diw6mR_UP44H@26p%5`jPqWFO3CTs@@zYMIKX@ zG>~G#&5rz<-Lx2>XEQO`7hK)bTI!9hrTFMs65mgY@HZ-QFib3FgmU9+G78+$)DB?f zW>NtN%)AEo1xT%U=qK@Zy_=$oW?WjJNztY+UdfT|O)S10mOw2HOYK8Si;kb96m~e# zq#Y#H`1+_S2~^3Es-?n>rR3)XIhSYM_=vBkOSJTb~m<*Jflm-?NGPKtTi6 z+?O(!cxTY{2GwP|)A}KV1JL`AuSc%=?rm9ZQ&>Z|fnYt7p4}a6IB51p8c|bc5YU{% zp)ddCs5&Rhb?eHaWYEig6Z+`6p8E-@a+AAN`3>HE{ui${3>M=V&E_DB$HWddB8^97 zF@PGCd=1KGI$je%X%*$Maq8H8oVcF9c_j00#V8QND92mce!bsV&;CQV0#&vrm;;lS zyG^qjSMRc72nlT@H)=^Ot_76U`DDpLzW>@30*&=Sq%^VjO+`S@ew$F4XF+U-Q`e zQ?vDs;Fehtm1!MI@tXI)LZW5PkB#{^6Oz>eiGxYi-exaP|KO;+{!UxuK0NTRMgBpI zfc41ZdirNdVgG(K{VTg*4KU2O^`$0V!ugLxl3e;b8TFpynYvdi3TBZ7$p|)HL;0+K-_OXXbfjb_-2RT#zGvWp%h&c9Mxhsi;Q5; zirrY$&$1a$aK^;+S`32OwNT`|jPzm8DzYJPJ^BQa>D!4*rUGPhJ(_ddWc(4^YC?52 zW~29*xd=t>^d`d^RlMypbj1@$#HW(6gQYwqBOz6Mk*DNoCaCcK9~Kl5h`<;sqWiuF zIyUz&fid?LzCDS}(|+BCy%W4XK?o#^7L)bdW45-CuDC^BVW_tlGDYS5cU^4OKgzE? z&LJ=@QS@58jCrDmBHJ+0TA(Yx@M7BP;SrLc#@DHl6`bT^Q(usME8^LckQvT(s9gU; zj0zk^#6D@Ks1TQ4_50>Q8E-mez~YPkR}ca;6x(<~Od=3dE2}SDD#B0>`Ci>+3jLoU zAH$y)UqHX09Bmo9@@HYkl4T;L)i6R*3E$sW8pY0Lx+$NZ&?dV0c2SVZ<(BhQgDux; z$z(pJjl6API)D9+>8;FlNhV%uS3lzXqnFA$1ecm;iEkya zIoAzMZlr}Cdr^pX9TC>(OH6?5r(EwCxDpLL^zJx1>!E}El7&I9DW|pG6|r+)hpF3y z%in+XZ`P5~d~ro4>@@xH=gz+2a*xc_HdFc3IPJfoBahrWhKDyX?F|BorPYiyG4I5% z&MRw_S^NVr^wSI8(9+ai*!CUQ$a2TaA8H->R^6!Q?>2euAYAOXeSFkwbn!Bx3UW!e z``G#uY2{&J9=vhots2g=;bcMx=}gIk>^E!OJ&u1kcH$3s1_?5<+7gx_j`zYmI*>C% z_QCWb*Eoa(je*F4QD~+z3pya*0VoZIG##ywhaR5b`N+TBw4sALuhw5fd8qz!mDf<* zT+=j_q*b>q9a}J+qzyEK&pFZYh}~8q9A=BF`^J(lf>y#=iHv^Vr^=3LiH_*9KD0jb zhSWU{ZRAyz(Hk4}uG>@>90WEwFGs@3nS$PC+>*1ZV5j4^AT#G_S@sO`zR+B;L}=E0 z5*ah~DVxuh?sS(T96REndL_$j1oqX-l5<5XHLzJkWoY1Vp+bM7*{ISbosFcgSB^G~ zLoHp5j9%v~`keg05Skn@J!L2GMZ`b)o3x3+PQ1nsRE~PR+yC0% z4h|UPuM@IACh)Hi66Cd^#ga$psUOE3W)ib23k4$ppFxF83O6LM+$h?9G7S3`&q2VP zI^-TUfkElUFMc}_tIFx_bf?7R~ug=##5yyXZ>ls#b&52~9ZLi!~ z+3eI(`FFe4htPQS%n<=sqwy|l`{Q-e-z*40Czl>QrXl!`);D)fOfFa3a`U(BDJ%(g zt;@|4qW|Cx`KJ(PJ3_;wVtt)29YNt|{4m*!RFK~zkv7L4vnq@!!k?AL+fk{qcyDXj z59s6LyUm)b$1T>$<$#b=dK|R{#fJgJNhT&I->L3U9Fo=xL16Opxo$ggAN(Mu!e?V; zC0ES~vD|ux$^^;X9aB_5z`a~(L83Acw$ZjNPA^kk%A{%NekL-z!xv{=l6<`P&ud?X z^lHe$zaOsLTUNwB)at11!t@<1M9B%)x=z7^bjagv*&}K(nVBH`h)rrkzD` zWE(V4;TT4f65NYK3q9KxbfP|-MCC0g(?vIcoFd?_seFc`OalY?Fk0B{_BQ_Tygst$ zlhw<^rnl~o{owEVOc<;xyy7(v67Ip^E{@U6@O`6%`oszlSr=YJMpNlZ4~jC05V)r6@% znS#!>`{hx;AERyl4AcDFST7D4cGVSwv#;Kf=~?`XE}jP#?}NZnK(NpC_oU^)$Pd3&bKnCo)@QAGY6 zx#IP?we@1p-K~j?Fu$@64eOSi*wV&;!Cj-IVhe2Z54x#xynkIBio|E%ci}BSa%coW zi@SNSJE^{nNgtjie?erby|{H02(3RB)ILu)P^k8ofSMxvsPUKYN)IZ@(bN_PLXQZ@`RCjl*c3 zwyOJ`ml}ngQw>(GZbx9DDh50VSxgrK)hqQb!VIUZj6SL1+v-{`MV5tOkc`l}a+_-^@ zLLG(lZYaH=E2=y9+h(Apg@tLm<6To#*nc)pzr zeB>XG1KGgz>P?$@vp1c8f=}XxW=xYjDb_hK>=&d#Fs?G zS`?l5RRl*X7Xc?9|8=!I{kX3sgAxfQa8L!V)r~CJ&W`5ohs`q z0RWc!=OgO%GJQX-r3J#6q5gVShV}DR|%BpsHorsIDcPoqgOEe=MPMn02py`_+rz! zwWz@ob>uXp7#t3oy2iJ;6e5W0D@J>JKH!q{nku#)jPz|`v3KC=J8Oie;l7-EF;S>(vW2=nUE#Ah>Nj>;T6iB znvq-93`*8%G?pUUO;?Gsg;r!q$THcrAVM`+qD;0SnK8r6+%xlg-QS<*b%33=esSC5Ag5&_uax8-;-s#Ts>AeKSj=ca%N9u4+?z z0{BFqnkf5`2nF_@&JZpK9`vdaJL=*%l<|b?__3{`$$MCSg&DM{0&-oM8ysG2I9aD| z2&?epiXIr$j?(2k$;e#lv;(G)44Ib(eDB$KTcN9dqJ)=b&4BuwEBS}ZSG;ia!&BtR zgCsB2&{C~S+TSvUb1aai`&!nk9XBl8Jh+u9X9W%&4sYEkn)#5ahqK8E9~oRhJIRTC zO_W%|Fzd1YTh$-X6)#uz)zZtVu(xmI$@sbS}BsG`%Xn;JYzEdVJ)H0xVA*u*Z-W|hDu z{9T~q9y0%i5ZmZXzxs4{zEQ|w2~0xgx!8p4ocf`%rd4>wc^^+-y*uXV${F#fpuKob z_zp!UhmT&!y&%hk4sQvEo%1FdP8_Ox(GeqXGiHgII4IT~u521KiXpWD;ce4nAeXTx8po2Tzp zD5PP&wF;t zELo|h3Mm!oV>W^RD{c2^r?*i$KEXT*y{Vz+*V!;(P`Q>v-Ks6wwG3=`1q|b>lHEN`WooY3{5Or!OqxX5=0IDWSW{p151qlw)K#^*=uFqM==<` zrsHD(lsJy0!0NfLhlW=><9_8ohqt>gEQ=1kwf17f7pdAbk0$y1vin5=<)bv`=VKHx z9k&+1=^XwPd#>lNHvMNdwU%9>Vify3D62`S*^8i3_s3J5z*IoUFW%r8(@HSkEmi^48Xioq-TY@f7@Xf}mRal$k7EdC69mFaOk1_M2ck?;mR}Y593gS6w-0 z-s!!QL1 z2z@?hOjHichjMPL)`#Vtnj=UiXF*=NeS7=KAyGwsZD3D25FYvv*Q(7;8kUATKoP7# z*?^GE#gWk;v>TiFBtTYzn0NLr=31PKyjHTSN3V533Vy^e4}x~U$!;PeFix~aiatUN z7?c+kX%D5eR}-Kos0;Z8Fz>!?M^M)fu%zrXH;y+7`;EBsxjB(kAzNM-LJ_ihmd zfM5>6(F(R0#&(II?SD$V33>q z`YvHt2dp8-;l^0ycL(>RXY+Y3|G?{Q@al#)OgI7;*0B3K?^|wN!Bpi7hbsL(GvN{1TunOmbb@y z2u-~6yL%3uGNKyy;v{>_QN)+eMG2q=UqtPI6)KYud0#TP)wcbZZ9EBRiqtt^b>2k# z({JGNQ_t(Pwk>d6<$#(Dp01e08ZJx0veDv^$FRhJOX@lZA>NIopP8GTLCNwh*-&)) zdedmuW1I)CSFF3LzXyFeEJ{^4xx$6=7&6!WMkA6FAulOJdn0Vpa(;)=5`eq`;;Rdi zh4~6Kkm~Z1s1e7kaUneWd*G7FTRMbi;U=Xy6FbL_0)QPJaj2)ihA=JE&A)oq< zH*b;i2?E-!`2V;@IG|I6lYw2xntN^~?g-#H$@)E=7P?8RPGVbD{z17!e4 zA)c_d1TD(Xa)GA-XHw>%qoYTq8ik0y)_q Kx2v(ik^TiMONt-> literal 0 HcmV?d00001 diff --git a/src/renderer/src/assets/images/models/palm.svg b/src/renderer/src/assets/images/models/palm.svg new file mode 100644 index 00000000..5c345fe1 --- /dev/null +++ b/src/renderer/src/assets/images/models/palm.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/src/assets/images/providers/gemini.png b/src/renderer/src/assets/images/providers/gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..05bce50733711f2eee1c7e2d185df98cd968b1ec GIT binary patch literal 19259 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 Kx2v(ik^TiMONt-> literal 0 HcmV?d00001 diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index ac46e15a..c8569bf8 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -33,6 +33,22 @@ export const SYSTEM_MODELS: Record = { enabled: true } ], + gemini: [ + { + id: 'gemini-1.5-flash', + provider: 'gemini', + name: 'Gemini 1.5 Flash', + group: 'Gemini 1.5', + enabled: true + }, + { + id: 'gemini-1.5-pro-exp-0801', + provider: 'gemini', + name: 'Gemini 1.5 Pro Experimental 0801', + group: 'Gemini 1.5', + enabled: true + } + ], silicon: [ { id: 'Qwen/Qwen2-7B-Instruct', diff --git a/src/renderer/src/config/provider.ts b/src/renderer/src/config/provider.ts index c01e276f..6d912179 100644 --- a/src/renderer/src/config/provider.ts +++ b/src/renderer/src/config/provider.ts @@ -3,10 +3,13 @@ import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.jpeg' import ChatGPTModelLogo from '@renderer/assets/images/models/chatgpt.jpeg' import ClaudeModelLogo from '@renderer/assets/images/models/claude.png' import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png' +import EmbeddingModelLogo from '@renderer/assets/images/models/embedding.png' +import GeminiModelLogo from '@renderer/assets/images/models/gemini.png' import GemmaModelLogo from '@renderer/assets/images/models/gemma.jpeg' import LlamaModelLogo from '@renderer/assets/images/models/llama.jpeg' import MicrosoftModelLogo from '@renderer/assets/images/models/microsoft.png' import MixtralModelLogo from '@renderer/assets/images/models/mixtral.jpeg' +import PalmModelLogo from '@renderer/assets/images/models/palm.svg' import QwenModelLogo from '@renderer/assets/images/models/qwen.png' import YiModelLogo from '@renderer/assets/images/models/yi.svg' import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.jpg' @@ -14,6 +17,7 @@ import AnthropicProviderLogo from '@renderer/assets/images/providers/anthropic.j import BaichuanProviderLogo from '@renderer/assets/images/providers/baichuan.png' import DashScopeProviderLogo from '@renderer/assets/images/providers/dashscope.png' import DeepSeekProviderLogo from '@renderer/assets/images/providers/deepseek.png' +import GeminiProviderLogo from '@renderer/assets/images/providers/gemini.png' import GroqProviderLogo from '@renderer/assets/images/providers/groq.png' import MoonshotProviderLogo from '@renderer/assets/images/providers/moonshot.jpeg' import MoonshotModelLogo from '@renderer/assets/images/providers/moonshot.jpeg' @@ -52,6 +56,8 @@ export function getProviderLogo(providerId: string) { return AnthropicProviderLogo case 'aihubmix': return AiHubMixProviderLogo + case 'gemini': + return GeminiProviderLogo default: return undefined } @@ -75,7 +81,11 @@ export function getModelLogo(modelId: string) { moonshot: MoonshotModelLogo, phi: MicrosoftModelLogo, baichuan: BaichuanModelLogo, - claude: ClaudeModelLogo + claude: ClaudeModelLogo, + gemini: GeminiModelLogo, + embedding: EmbeddingModelLogo, + bison: PalmModelLogo, + palm: PalmModelLogo } for (const key in logoMap) { @@ -242,5 +252,17 @@ export const PROVIDER_CONFIG = { docs: 'https://doc.aihubmix.com/', models: 'https://aihubmix.com/models' } + }, + gemini: { + api: { + url: 'https://generativelanguage.googleapis.com', + editable: false + }, + websites: { + official: 'https://gemini.google.com/', + apiKey: 'https://aistudio.google.com/app/apikey', + docs: 'https://ai.google.dev/gemini-api/docs', + models: 'https://ai.google.dev/gemini-api/docs/models/gemini' + } } } diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 0d0a7641..fe22c8c0 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -106,6 +106,7 @@ const resources = { }, provider: { openai: 'OpenAI', + gemini: 'Gemini', deepseek: 'DeepSeek', moonshot: 'Moonshot', silicon: 'SiliconFlow', @@ -323,6 +324,7 @@ const resources = { }, provider: { openai: 'OpenAI', + gemini: 'Gemini', deepseek: '深度求索', moonshot: '月之暗面', silicon: '硅基流动', diff --git a/src/renderer/src/services/ProviderSDK.ts b/src/renderer/src/services/ProviderSDK.ts index fe17ae41..76df0640 100644 --- a/src/renderer/src/services/ProviderSDK.ts +++ b/src/renderer/src/services/ProviderSDK.ts @@ -1,10 +1,12 @@ import Anthropic from '@anthropic-ai/sdk' import { MessageCreateParamsNonStreaming, MessageParam } from '@anthropic-ai/sdk/resources' +import { GoogleGenerativeAI } from '@google/generative-ai' import { DEFAULT_MAX_TOKENS } from '@renderer/config/constant' import { getOllamaKeepAliveTime } from '@renderer/hooks/useOllama' import { Assistant, Message, Provider, Suggestion } from '@renderer/types' import { removeQuotes } from '@renderer/utils' -import { sum, takeRight } from 'lodash' +import axios from 'axios' +import { isEmpty, sum, takeRight } from 'lodash' import OpenAI from 'openai' import { ChatCompletionCreateParamsNonStreaming, ChatCompletionMessageParam } from 'openai/resources' @@ -15,6 +17,7 @@ export default class ProviderSDK { provider: Provider openaiSdk: OpenAI anthropicSdk: Anthropic + geminiSdk: GoogleGenerativeAI constructor(provider: Provider) { this.provider = provider @@ -22,12 +25,17 @@ export default class ProviderSDK { const baseURL = host.endsWith('/') ? host : `${provider.apiHost}/v1/` this.anthropicSdk = new Anthropic({ apiKey: provider.apiKey, baseURL }) this.openaiSdk = new OpenAI({ dangerouslyAllowBrowser: true, apiKey: provider.apiKey, baseURL }) + this.geminiSdk = new GoogleGenerativeAI(provider.apiKey) } private get isAnthropic() { return this.provider.id === 'anthropic' } + private get isGemini() { + return this.provider.id === 'gemini' + } + private get keepAliveTime() { return this.provider.id === 'ollama' ? getOllamaKeepAliveTime() : undefined } @@ -42,7 +50,6 @@ export default class ProviderSDK { const { contextCount, maxTokens } = getAssistantSettings(assistant) const systemMessage = assistant.prompt ? { role: 'system', content: assistant.prompt } : undefined - const userMessages = takeRight(messages, contextCount + 1).map((message) => ({ role: message.role, content: message.content @@ -66,25 +73,64 @@ export default class ProviderSDK { } }) ) - } else { - // @ts-ignore key is not typed - const stream = await this.openaiSdk.chat.completions.create({ + return + } + + if (this.isGemini) { + const geminiModel = this.geminiSdk.getGenerativeModel({ model: model.id, - messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[], - stream: true, - temperature: assistant?.settings?.temperature, - max_tokens: maxTokens, - keep_alive: this.keepAliveTime + systemInstruction: assistant.prompt, + generationConfig: { + maxOutputTokens: maxTokens, + temperature: assistant?.settings?.temperature + } }) - for await (const chunk of stream) { + + const userLastMessage = userMessages.pop() + + const chat = geminiModel.startChat({ + history: userMessages.map((message) => ({ + role: message.role === 'user' ? 'user' : 'model', + parts: [{ text: message.content }] + })) + }) + + const userMessagesStream = await chat.sendMessageStream(userLastMessage?.content!) + + for await (const chunk of userMessagesStream.stream) { if (window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED)) break - onChunk({ text: chunk.choices[0]?.delta?.content || '', usage: chunk.usage }) + onChunk({ + text: chunk.text(), + usage: { + prompt_tokens: chunk.usageMetadata?.promptTokenCount || 0, + completion_tokens: chunk.usageMetadata?.candidatesTokenCount || 0, + total_tokens: chunk.usageMetadata?.totalTokenCount || 0 + } + }) } + + return + } + + // @ts-ignore key is not typed + const stream = await this.openaiSdk.chat.completions.create({ + model: model.id, + messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[], + stream: true, + temperature: assistant?.settings?.temperature, + max_tokens: maxTokens, + keep_alive: this.keepAliveTime + }) + + for await (const chunk of stream) { + if (window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED)) break + onChunk({ text: chunk.choices[0]?.delta?.content || '', usage: chunk.usage }) } } public async translate(message: Message, assistant: Assistant) { const defaultModel = getDefaultModel() + const { maxTokens } = getAssistantSettings(assistant) const model = assistant.model || defaultModel const messages = [ { role: 'system', content: assistant.prompt }, @@ -99,17 +145,34 @@ export default class ProviderSDK { temperature: assistant?.settings?.temperature, stream: false }) + return response.content[0].type === 'text' ? response.content[0].text : '' - } else { - // @ts-ignore key is not typed - const response = await this.openaiSdk.chat.completions.create({ - model: model.id, - messages: messages as ChatCompletionMessageParam[], - stream: false, - keep_alive: this.keepAliveTime - }) - return response.choices[0].message?.content || '' } + + if (this.isGemini) { + const geminiModel = this.geminiSdk.getGenerativeModel({ + model: model.id, + systemInstruction: assistant.prompt, + generationConfig: { + maxOutputTokens: maxTokens, + temperature: assistant?.settings?.temperature + } + }) + + const { response } = await geminiModel.generateContent(message.content) + + return response.text() + } + + // @ts-ignore key is not typed + const response = await this.openaiSdk.chat.completions.create({ + model: model.id, + messages: messages as ChatCompletionMessageParam[], + stream: false, + keep_alive: this.keepAliveTime + }) + + return response.choices[0].message?.content || '' } public async summaries(messages: Message[], assistant: Assistant): Promise { @@ -134,18 +197,41 @@ export default class ProviderSDK { }) return message.content[0].type === 'text' ? message.content[0].text : null - } else { - // @ts-ignore key is not typed - const response = await this.openaiSdk.chat.completions.create({ + } + + if (this.isGemini) { + const geminiModel = this.geminiSdk.getGenerativeModel({ model: model.id, - messages: [systemMessage, ...userMessages] as ChatCompletionMessageParam[], - stream: false, - max_tokens: 50, - keep_alive: this.keepAliveTime + systemInstruction: systemMessage.content, + generationConfig: { + temperature: assistant?.settings?.temperature + } }) - return removeQuotes(response.choices[0].message?.content || '') + const lastUserMessage = userMessages.pop() + + const chat = await geminiModel.startChat({ + history: userMessages.map((message) => ({ + role: message.role === 'user' ? 'user' : 'model', + parts: [{ text: message.content }] + })) + }) + + const { response } = await chat.sendMessage(lastUserMessage?.content!) + + return response.text() } + + // @ts-ignore key is not typed + const response = await this.openaiSdk.chat.completions.create({ + model: model.id, + messages: [systemMessage, ...userMessages] as ChatCompletionMessageParam[], + stream: false, + max_tokens: 50, + keep_alive: this.keepAliveTime + }) + + return removeQuotes(response.choices[0].message?.content || '') } public async suggestions(messages: Message[], assistant: Assistant): Promise { @@ -172,6 +258,7 @@ export default class ProviderSDK { public async check(): Promise<{ valid: boolean; error: Error | null }> { const model = this.provider.models[0] + const body = { model: model.id, messages: [{ role: 'user', content: 'hi' }], @@ -182,13 +269,32 @@ export default class ProviderSDK { try { if (this.isAnthropic) { const message = await this.anthropicSdk.messages.create(body as MessageCreateParamsNonStreaming) - return { valid: message.content.length > 0, error: null } - } else { - const response = await this.openaiSdk.chat.completions.create(body as ChatCompletionCreateParamsNonStreaming) - return { valid: Boolean(response?.choices[0].message), error: null } + return { + valid: message.content.length > 0, + error: null + } + } + + if (this.isGemini) { + const geminiModel = this.geminiSdk.getGenerativeModel({ model: body.model }) + const result = await geminiModel.generateContent(body.messages[0].content) + return { + valid: !isEmpty(result.response.text()), + error: null + } + } + + const response = await this.openaiSdk.chat.completions.create(body as ChatCompletionCreateParamsNonStreaming) + + return { + valid: Boolean(response?.choices[0].message), + error: null } } catch (error: any) { - return { valid: false, error } + return { + valid: false, + error + } } } @@ -198,6 +304,22 @@ export default class ProviderSDK { return [] } + if (this.isGemini) { + const api = this.provider.apiHost + '/v1beta/models' + const { data } = await axios.get(api, { params: { key: this.provider.apiKey } }) + return data.models.map( + (m: any) => + ({ + id: m.name.replace('models/', ''), + name: m.displayName, + description: m.description, + object: 'model', + created: Date.now(), + owned_by: 'gemini' + }) as OpenAI.Models.Model + ) + } + const response = await this.openaiSdk.models.list() return response.data } catch (error) { diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index 8084636a..a0766664 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -22,7 +22,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 20, + version: 21, blacklist: ['runtime'], migrate }, diff --git a/src/renderer/src/store/llm.ts b/src/renderer/src/store/llm.ts index b2a848b2..a8ad36cc 100644 --- a/src/renderer/src/store/llm.ts +++ b/src/renderer/src/store/llm.ts @@ -31,6 +31,15 @@ const initialState: LlmState = { isSystem: true, enabled: true }, + { + id: 'gemini', + name: 'Gemini', + apiKey: '', + apiHost: 'https://generativelanguage.googleapis.com', + models: SYSTEM_MODELS.gemini.filter((m) => m.enabled), + isSystem: true, + enabled: false + }, { id: 'silicon', name: 'Silicon', diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index d49c863f..b678ab6a 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -296,6 +296,26 @@ const migrateConfig = { fontSize: 14 } } + }, + '21': (state: RootState) => { + return { + ...state, + llm: { + ...state.llm, + providers: [ + ...state.llm.providers, + { + id: 'gemini', + name: 'Gemini', + apiKey: '', + apiHost: 'https://generativelanguage.googleapis.com', + models: SYSTEM_MODELS.gemini.filter((m) => m.enabled), + isSystem: true, + enabled: false + } + ] + } + } } } diff --git a/yarn.lock b/yarn.lock index 49b8f8c6..c1b5f01b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -961,6 +961,13 @@ __metadata: languageName: node linkType: hard +"@google/generative-ai@npm:^0.16.0": + version: 0.16.0 + resolution: "@google/generative-ai@npm:0.16.0" + checksum: 10c0/5d561a41cb7be60fc9b49965b66359e15df907bf6679009de7917beff138ba69d4a0772ab2a9d6f0e543d658d72bd19b83e6abdb87a6cdfa402a8764b08eed4c + languageName: node + linkType: hard + "@hello-pangea/dnd@npm:^16.6.0": version: 16.6.0 resolution: "@hello-pangea/dnd@npm:16.6.0" @@ -3099,6 +3106,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.7.3": + version: 1.7.3 + resolution: "axios@npm:1.7.3" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/a18cbe559203efa05fb1fec2d1898e23bf6329bd2575784ee32aa11b5bbe1d54b9f472c49a261294125519cf62aa4fe5ef6e647bb7482eafc15bffe15ab314ce + languageName: node + linkType: hard + "bail@npm:^2.0.0": version: 2.0.2 resolution: "bail@npm:2.0.2" @@ -3446,6 +3464,7 @@ __metadata: "@electron-toolkit/preload": "npm:^3.0.0" "@electron-toolkit/tsconfig": "npm:^1.0.1" "@electron-toolkit/utils": "npm:^3.0.0" + "@google/generative-ai": "npm:^0.16.0" "@hello-pangea/dnd": "npm:^16.6.0" "@kangfenmao/keyv-storage": "npm:^0.1.0" "@reduxjs/toolkit": "npm:^2.2.5" @@ -3457,6 +3476,7 @@ __metadata: "@vitejs/plugin-react": "npm:^4.2.1" ahooks: "npm:^3.8.0" antd: "npm:^5.18.3" + axios: "npm:^1.7.3" browser-image-compression: "npm:^2.0.2" dayjs: "npm:^1.11.11" dotenv-cli: "npm:^7.4.2" @@ -5037,6 +5057,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.6": + version: 1.15.6 + resolution: "follow-redirects@npm:1.15.6" + peerDependenciesMeta: + debug: + optional: true + checksum: 10c0/9ff767f0d7be6aa6870c82ac79cf0368cd73e01bbc00e9eb1c2a16fbb198ec105e3c9b6628bb98e9f3ac66fe29a957b9645bcb9a490bb7aa0d35f908b6b85071 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -8251,6 +8281,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0"