From 826ef00da30738c458772ae93f8d4d7f10b9231c Mon Sep 17 00:00:00 2001 From: Sean Hatfield <seanhatfield5@gmail.com> Date: Thu, 16 May 2024 13:56:28 -0700 Subject: [PATCH] [FEAT] LiteLLM provider support (#1424) * litellm LLM provider support * fix lint error * change import orders fix issue with model retrieval --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com> --- README.md | 1 + docker/.env.example | 6 + .../LLMSelection/LiteLLMOptions/index.jsx | 148 +++++++++++++++ frontend/src/media/llmprovider/litellm.png | Bin 0 -> 50535 bytes .../GeneralSettings/LLMPreference/index.jsx | 14 +- .../Steps/DataHandling/index.jsx | 9 + .../Steps/LLMPreference/index.jsx | 14 +- server/.env.example | 6 + server/models/systemSettings.js | 6 + server/utils/AiProviders/liteLLM/index.js | 178 ++++++++++++++++++ server/utils/helpers/customModels.js | 22 +++ server/utils/helpers/index.js | 3 + server/utils/helpers/updateENV.js | 19 ++ 13 files changed, 422 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx create mode 100644 frontend/src/media/llmprovider/litellm.png create mode 100644 server/utils/AiProviders/liteLLM/index.js diff --git a/README.md b/README.md index e15f7ff65..bf50f209a 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Some cool features of AnythingLLM - [Groq](https://groq.com/) - [Cohere](https://cohere.com/) - [KoboldCPP](https://github.com/LostRuins/koboldcpp) +- [LiteLLM](https://github.com/BerriAI/litellm) - [Text Generation Web UI](https://github.com/oobabooga/text-generation-webui) **Embedder models:** diff --git a/docker/.env.example b/docker/.env.example index 70059ea51..7fedf944c 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -82,6 +82,12 @@ GID='1000' # GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096 # GENERIC_OPEN_AI_API_KEY=sk-123abc +# LLM_PROVIDER='litellm' +# LITE_LLM_MODEL_PREF='gpt-3.5-turbo' +# LITE_LLM_MODEL_TOKEN_LIMIT=4096 +# LITE_LLM_BASE_PATH='http://127.0.0.1:4000' +# LITE_LLM_API_KEY='sk-123abc' + # LLM_PROVIDER='cohere' # COHERE_API_KEY= # COHERE_MODEL_PREF='command-r' diff --git a/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx b/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx new file mode 100644 index 000000000..6199ba26d --- /dev/null +++ b/frontend/src/components/LLMSelection/LiteLLMOptions/index.jsx @@ -0,0 +1,148 @@ +import { useEffect, useState } from "react"; +import System from "@/models/system"; + +export default function LiteLLMOptions({ settings }) { + const [basePathValue, setBasePathValue] = useState(settings?.LiteLLMBasePath); + const [basePath, setBasePath] = useState(settings?.LiteLLMBasePath); + const [apiKeyValue, setApiKeyValue] = useState(settings?.LiteLLMAPIKey); + const [apiKey, setApiKey] = useState(settings?.LiteLLMAPIKey); + + return ( + <div className="w-full flex flex-col gap-y-4"> + <div className="w-full flex items-center gap-4"> + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-4"> + Base URL + </label> + <input + type="url" + name="LiteLLMBasePath" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="http://127.0.0.1:4000" + defaultValue={settings?.LiteLLMBasePath} + required={true} + autoComplete="off" + spellCheck={false} + onChange={(e) => setBasePathValue(e.target.value)} + onBlur={() => setBasePath(basePathValue)} + /> + </div> + <LiteLLMModelSelection + settings={settings} + basePath={basePath} + apiKey={apiKey} + /> + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-4"> + Token context window + </label> + <input + type="number" + name="LiteLLMTokenLimit" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="4096" + min={1} + onScroll={(e) => e.target.blur()} + defaultValue={settings?.LiteLLMTokenLimit} + required={true} + autoComplete="off" + /> + </div> + </div> + <div className="w-full flex items-center gap-4"> + <div className="flex flex-col w-60"> + <div className="flex flex-col gap-y-1 mb-4"> + <label className="text-white text-sm font-semibold flex items-center gap-x-2"> + API Key <p className="!text-xs !italic !font-thin">optional</p> + </label> + </div> + <input + type="password" + name="LiteLLMAPIKey" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="sk-mysecretkey" + defaultValue={settings?.LiteLLMAPIKey ? "*".repeat(20) : ""} + autoComplete="off" + spellCheck={false} + onChange={(e) => setApiKeyValue(e.target.value)} + onBlur={() => setApiKey(apiKeyValue)} + /> + </div> + </div> + </div> + ); +} + +function LiteLLMModelSelection({ settings, basePath = null, apiKey = null }) { + const [customModels, setCustomModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + if (!basePath) { + setCustomModels([]); + setLoading(false); + return; + } + setLoading(true); + const { models } = await System.customModels( + "litellm", + typeof apiKey === "boolean" ? null : apiKey, + basePath + ); + setCustomModels(models || []); + setLoading(false); + } + findCustomModels(); + }, [basePath, apiKey]); + + if (loading || customModels.length == 0) { + return ( + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-4"> + Chat Model Selection + </label> + <select + name="LiteLLMModelPref" + disabled={true} + className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5" + > + <option disabled={true} selected={true}> + {basePath?.includes("/v1") + ? "-- loading available models --" + : "-- waiting for URL --"} + </option> + </select> + </div> + ); + } + + return ( + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-4"> + Chat Model Selection + </label> + <select + name="LiteLLMModelPref" + required={true} + className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5" + > + {customModels.length > 0 && ( + <optgroup label="Your loaded models"> + {customModels.map((model) => { + return ( + <option + key={model.id} + value={model.id} + selected={settings.LiteLLMModelPref === model.id} + > + {model.id} + </option> + ); + })} + </optgroup> + )} + </select> + </div> + ); +} diff --git a/frontend/src/media/llmprovider/litellm.png b/frontend/src/media/llmprovider/litellm.png new file mode 100644 index 0000000000000000000000000000000000000000..da4faf5b5ca6c812a73e3c1a74d93ea6f6afa682 GIT binary patch literal 50535 zcmV(?K-a&CP)<h;3K|Lk000e1NJLTq0077U0077c0ssI2B04$A004jhNkl<Zc%0n* zX}2WFjUEUCu*H{%xJzc%UQM#ut@r&hGe2d1_<Yeh8cAm)bvLV7tg5Ws?ivy9W(Hu6 z!QA6UW)|5+jZ_rr3}(fR#lzhU20Q>Bz<mC1|ED4d3KIx|A`l=6IUy2{;6aHInLw4n z3FxexKyq|0gs4J=RTYW6ps1n(5CKS0DGCVa08oJx3DMavhyo}8Ak^dO+38SxdP(5J zL(je_g6;E4^<`011+9PAC-p2U?LS!WtUe=Bk6NFTy5%kV2B1LwE(3D?ZhhAgkSf{9 zRNYYl1W}L_ynsXLQ0oxc)ntT%$n|vfSDAnT1rhBwT>U|Nh>en6Ouarrt*5jL1zc`V zRZlnE&hY0RY6sQH_@8x%)Vr(GZ&!GJo&Kk;==?quQ1SFeowR!2h_p;Bh}iYRDlXc7 z&wd80NP#@b&N5g}g0cLeA6>)Gyfq}dqV?oxS7%qDxIDGj*WWz9iuR#4mrt*r&FAS4 zQT$lMmwT-b6ObyxXn&qwUp{j>*VDtUgU)}gUGLw_;n%xCSXOv@;WYK80NUGaFc8rZ z+NiLI$TF@1s)DkNeqHQD&b}>8Rp`^Ou7`f4s!p%}sbjg+^*Q2tB3UjN^yw2PnQ0=0 z=Rc|IOQu2AMS&+lt;$spS2>VXB2L1gP<7IBj{1BNCI&ncyZQ-d7rlIIl~`%RBI}EP zONXx-X<gG~V~KhuY>S{Y1*Fv)Le;KR{|1(7GwM84(|}?Yqp7PZfA!ERdTf>ua24FM zrN7MohwA#@8fAR{8R~lqXR#^O7@V}usvb>Uqb~m!H8Isyw)7->3iWQP2EtV)<V@=| zp|R7M4qt?<*;5odY5P-G9k1&`Jq@WY|Kgb@)xQzF<=1g)Q-JuMTG3V0oY^azEJ7m0 zEK*l7I&e;bKv}DVoz%vnsZ6E(1d=-Y^J5_OCy)U?Db%yGDxM~a+FN!@`jG;}r+Zs9 z@tI)hy08|IgD6$Fs+l_<8d`M^qam|Jpg*>ltFc{{Y<-tbDz;uKuETn|y1xn8Y8dMr z^B|t44&PfuHabnJkj`*2ks}HX`c|<r^JkiCv5-$Qi9Z)Q{qf@*EiKoFTG8+MsddpY zPw4&ZJ$yRTlSOSJ_4GdC`MPR8iJtHPt{`C*BRx}^cy_7pUH7^U3|g3-(DN0H%kca$ zfbiFKJb`yJ3UvjYy}PXD1$|eDUdNG_fkCe0?U_9KSX8q%dxt2c${@(H>dhAt5>UeP z2}O8PY#$!-{><AoEUOt$a=rrZ<;vwGHhQvOfZ%kwk0%qq=pJ13#7X=Y{nE_i`ZLXT zHwitB4(Um@;C)52{9DK8>8*2VX&R2~3(|ZkeKH7t3sQA1SSJ94@0$)?mXO^#ty9K} z@jxYq=n7yGnMEC;L-GdO)eI%!viR4D!}namKeamFS8u0T(~~G7u84^2WiplW<X+h` zJQHL5;J)zV`NReXDr(fHS;s}8;hBet_~9KqJ!$J{ftG4Q^t6uPOpxsQ)!gURX?nWR zpD};`%N{=!Za5u!8bVzZ^|HcImv}-@@>waRa!7OH9KGutf~yG3jH4{Wgo;nYZ70V6 z4xRtxtFBMZ-)QQ?t)9I}wEpz$tdh6RH0lWrs=g;N`sBo|j)I=A4O}I5^;Hq|OeHpZ zCkp~Rjq7svCuN3br-Kjf?XUCj=hIuqLe6FBd_bi|5}|rZB=x_<NUZLV`Jv>y{XTOV z`mpOGbk3VwSfN<`;-a<i1hW2ob&q<kaHQ#0Ts*4NSrMxJUF}@EDSXeF!!xaL!bbQs zsms&V5VA#G7ZIrN(m>{_!_BE|@IlYNSRe9)(Cs_s52CHKHJ;I2PCP-Z|Bm_lU-s}H zPcl9!d1$`$qO_|5QXAYl#G<6?vf|@h-W~Su9v+LNZSDXhP#3(5EkgPf8~hMK{8<C} z$3m#9V4YBPT^fkZLqj-;#F^)C?%kilm8c&CYH59emr<xEZ($a)lQEuO$&(g)&ob&_ zJ+XlRJ>e?*(W=StTuFTpWBV(XwVyPFc!DFVUuanmwKcjdO+xZOF3shBe|U3ufAjcQ zhn%qQXbRY22;P%(RR^-_|FFRFnQl3Gdn<@J*+5w|0qO@*)NbktP&Ku(WMp(*R39yZ z(=4M!RV-eBsPDbCMyD22+4N=$e4t_rE6`&5P8ef31Ag#JdQ!B1wHDFK>ZpGZ3OLyl zd<sCWL2b2QDw3j`OL{!ckNfH2cns0^(od{Yk->5A%^GU8n^nW1E%(*^T5TIDeh_Lp zx4`cQLf;SC+axZ@mNnuJ*d`vGw&Wl_;iwe?t<d>oT-8ClAxjgQ#E_m`IW=K=(to%_ z{1)sb4RslcT%s{2msS_+uT8yGpBh)_r5}dc1<scZemE+BZHE`c59hEQ_(6DP(c~;j zgjE%h7oemtr}A((PNmS$X^33mJY*I%1Y*%FYN=>Dh2T>X&DQi8csfVUAD9)A7O}!J z+%6TqwM4HLi&{Ee>{J!{C9{k~RaM}c=xJG50W2+{-0+)ZsrnXCTJG@)z@ut2StMdk zuGBSR*R4&~1XNmZ$~+crPV4Fq;L}yhld$N5s&rN5dgh$aU$d@ihhGCft22GBm(LE% zlP`FnjxmuCiDD6$bD45dLiLV9K=Kx9&=x*5TuV=Sy{+VR;l7u|Uj(bANj@A`e1hqw zFmX|*r-;xJ`BqpHG>G64kctB(g2vQvtNX_FLB$}H9m1mOs*Ym|xq%TAOpnRp*OI|> z^LUrMAKIz$<dWJUtNYU=hZcXaB2=QMP#mtyc^&oD<^C&1R!@ijQ_<q}Xw%$i4As@c zQyjWtu2eV`fgw`p6dgEk`l8B{EIvQ#$wLBpvVBVs_33BHgtz_NlEFI@b27I|+gppR zIOT)MFg00DsfvTsfa*~l6<;zi2b7>p%9axVSJwr3h2lI>AadxO0u-f$k`>r_hbqFk zN+pqq7U7n6aDdEQu^l$~LBo=!xTqOhJy%0Z9E(hyHIP?Tcq3-CAUyu*Dfp{9{0F!3 zld+}KWLI6~7CkSrfTYY?SRFb+Uz8LaxN4NNF|<S$Nn8Q*S=haWd7cbUliE5#!asGj zx_+_ceJzb=8j&y%L{%pYT9r{m?wmx>04eHG(q8Ai&WE`qmdq{Rgid1ThK(OCN52ie z18@`hqvGToyDA0DQV`V>6cn8qK%DO!5=*Jt$~jy^2EwchjF$ynRB5-h()_^|aFtVJ z^hs$|wfM37`qzB;vk=bHD*H*<YB4=B5W^yHqGp+8D9oI>lsdZTJXK|ZEb44QBRxq2 zf-|MFMs)D~faOmGHE|&&z)IpXPs`T55h_q2A`GB`*b|fEpc9qZ%Z%k-kN4$xFVh3h zkBJjAYbj#F#oZ9bEpDFq{!+(X@S_WxE({(*0mwkboH(v(R)o}4qZweTz=_PEY#c}< zIMNd9Ty&}-E^VP=Q}?vqi6Q)j7_lxFT;jBUB&m8*-XG(H{{;_^XNlO;s|xS=B>Dsd z%>6N&SRFDn1%(cQN|u~UgX~phptHQcU<;3`(o^KUVGvS%FNC=Mn64p?mZW=Ph}mp4 zbkiI#bakepGHP~3$W!mQa~ySY^PN1tIXt{QPOs+avG9S)gqVq{7J^hHXU@Wk8R$`i z&?uMB`)8jIFTU=tJ{kRnphpPgJ5tEPxe89zPsGB_B``Ut=Dvai!=3eV$U>^i2w9*- zSAU@1a!cW_0XC5n*gPBgzlFoUpFXqWy=)<#-qVH3w1klrn%T493?LU_7W2PJz?JHB z`BE{DL1A5>)f#L*r8HJ9?gHgkBchGRR1u4cId$e2s&b2?q0Nv(k6^?Kaacyu9=YRb zoNn;p<^J{ux&1NiUr#at6;H)EgW6hE7C}ZS?l2z?^I@M4lRjR2;=cIJ6(2vv<af_| zw;?zB3JOCiAVLuPSdDZkEZ|(lW>xm9pe+!}xkg%87N9Y**gQW|Y>l;sDkK)|B3)FJ z{F}(?zmJ{#7d-w%uJFB#&a%eTBuAyWWrW6#sY6=;oK|(EERvavkZ}T?g_wk%<aG@Y z>Ibo_#?fw}u2YiP0##_<x2)lU<?C2Qs(2OWw8AMY10O>=xcfKr&G+f>Iv?LD?{`DF zAOP=t#s0ap69|G3^&SVjyy_)u-p{u=-QE9g`sau5Zoa+!>^IM!|Mq(GB6fsC5lM_T zn+=&&>@HA}G-6iEjaH11m_<q(t|m)_uD)-`N)0=<0dtNjIwP!0uHCHujrfD@@Tl-1 zq2cK`zt0s37!RwW(uTAVwB(W!_R%3okz6>5W`QhND(Wzipp`JGjXPk3x08KETnW%8 zt9M$X%fDv<#f9aC<WK`}B#tomaE$2|AKu8TKg{pGE9n+!8Y2t?gg%4_h`xx+!i5b8 z76d`+T~&ldOBcI`Bfft9-OF!YeRo0kucyZYfAJ5`FSL*7{dQ}XUs<vmYr!C6;{`2I zrv*pB69HodP39<68(GDOg{#uEWlCqhZL9D2n>qX`kfLWPhWEgHJC>+{;L2^XK%`Lf z4@-6uz&!cPJQtZolR^h|brV{IagEtG)r4mN>f~~se##yWR&}{t!;1gOf*BwPnTQ<} z4}0QAI=)Uf-=+JPGT%CvHoe;pZgUZ0=g5N~&m2<8i8E&?PPJsHnnzT!kh-YjE`CDb zNz1$6AK%^m({X?NEq#3%$8PAH=R6-7pqfArqDA2pB6aojBIcmB{5weX61k2TS%?(Z zR2Vw7oGTj)JHxnt^+D0|!{a%-;U^rzvrOxWh0@X<)ef3dWT};kH42x^nI*9#fvi}d zdWXhVUL(^h<F^G*PqdXY*4^^E=K-uG`LhVVR>2}9@u;3Sz~u8C9NwgN-#*^{Sf;x` zW!uM#i~f2Do4!hwk|e4hj+$ne6Ki74)oim=-;yXNbkJ?%m8T$heEt1*FaO(jC8ur^ zY4e5O41qet2vQUU+(L%21!GcUFI79KE|52!#6_|QTYeQQ{-Ua5kw{RASwInWC$IQ# zNU9c%^^t%3F&jWXu!}aD>KuAUee5>FuxHNGl*c~hQbdbrW-VATZ-Y(iH`l!WN(ayt z&BK}Qq@~1Tg=9;ja!oBOI7O#HBm#Fbx#L~Ae^u_^q_l@fW87Si-SxJ===|UjkTVJa zClK$*R|r>BiwKH(Qm$~@fp_AW*-OXD7x7t^lJE7u{@?#9qx#*~S6v_afk=xyra4P> zCtIA>G*b1By@?x@Dm+v@*6J;^xbjkogB7v4>Mdeck!8W^Poz8k+6l_5##&|REMtGF zFFf5Koou970bK>fh|exAUOc;=-n>3M+*0RU3~3mdHRqhb1&^`92lKI8B+=4wCM{28 z1t)mY)~+S+sCf%3Vizk`vstVv!&mrS5EAAGzInNS^@sUzqpHK$*T<&cj@_n@ee@oH zrObIcK$?pb2$4Eb7S5&2xtc5jAA>YcSD4uaj92}|!{hz$|NTEn$MMoZ{N(DB;JO~M zs4zMLEi8oSp#t&XsDc5N7K;}kCG=})m{xyQnnTS9;?!{qbJbyX*rH+pr+CVeXjmAC ziwCn}5Lh_@=70WMm1C!Z(qHoMxLUY>)5I@$+6W3(FTtwWJKSxDXV+J+Zr<JO_u)8q z+foYWYHTEtbmZ7UD=}_Z)mEph@v5KZ9U+_u&8O=UMMiV7h*p?aoJ>AF>U4*Bk02q2 zvG2w%cFuY4eF(nmdvc&0a-ieAW|zomh>=<HoTlScN-`lP@14U~#A_mDAKd72ilsli zoWK1K-v!d4e=(4w*!7VVnPqlW5WR!W>3|$T2}%`XkPJ{7%f=kW>QM@5wSXkx{X&SR zC8cvlMqJ8KEd8@kan}276D6m>V#{vP(}F)8A6Gs7(#M~qCa;Gs5c-tCui&8iN1!qI z-FA3hcH6Okb$6fVoaUTTo~GoKoN6E!(FI|m=1Ri41{t5CrGNaV^`t%pTfMrGaipkI zltSqd5AXDNi+psXG5T@rcYU{U6r8gPNXvkub5*Z6??NX<bP14G(}dKlp&*cmRFzYQ z#S?bOae&LikLd?^b$z+{`X8SUy<Zfk6jE{+RD$MiKKLrx)ss+V=3GD$dJWP07+kEE zLmfI~#ZslO#kX2HFrJDHWTiPv^L45NN+<WRMUr)8B`%3aQ+4?Me92!Nvi&T<LS2=} zG6F62DuB`j+6I5k+ZR{YKi=F|3_qu7e>@P91`lwg5Cl9`AFnQ5qUJl9(krW+t-J?m zX=~$HEzaW;f2EG6)nKcVGCBq=Iz8ma8%cX8GkRAqr;pol*b#OFq*!NpCE_VL7rTz< z#4N?q1kO42QGn}e$TR0s4WaW6VuD;<K<(ssJl>}1HcvNs7+jU^Lc&!@(GLg%&vE}Y zz4`v(@#Y9cq&l8Qk>k+&&33%FzS>;Gxb=Pn>H~TKO%Y30WC0*P2bj(7V{4(Ymc*{1 zL~I0c(Dwq2q#q;y_ayBv5?uXRg0-M#UDdHJ^_MLI5gm<P|LpSW#pT`Yan8r%6#c;i zQS|5|kyr8#U9h$qjgO@Z;Q`+xO6q%+U8f(j7ioz(I#m=Vt;pS%8Sd|n_cxhS3^aP$ zIOsrp)jA%%^UhK@rLaool2wXu5tb@g($eVGD!_O?rG+<Fb1f{0&Qmv~X}-%3H|g<p z*=}4=2SL37qj`4o1c&?i^>@b~zrDYCeOIAW)!PTuJPcuXS)M)f7uPagyW!e*mwvde zHWmZB_Dh=0ELzGFTV0uJY_FEgmh&Dc^<C8SESq_k_#t!WFP~5RnHp=I@RfCmOER@_ zLNGXgb#eLp>hhT0-pvyps_KYd6XNV7qRi-g9TM?c{c-~kHh#;&%`DJ@;!vMRtBW^f zpw*;G9TY@y$|240ct7v&v)7BEyFwg5Lrx`x;(h16cZ8~?Nam`I66c)fIhVYW&9RCS z*8>59cMQzgGK1CoP^!Kpai+w2|L*wi4~OmU;yP}8#p!goe|+`*!}s6bzI!zt?*MYp zxwmrN;7U}P)1$n5o9}-AnnIx=U4ONC_SMB_zj^-riwk$@psKteO*I=`aS5pq38DzM z^nO#~gr}YltC+1N+)pm{2{!x$>iVS*FKfesQ}ys!M~%}`S-G}LKoA!WyM>?;Av(C) zbT6(h?)Q(!JnP{&A$p>K&N+|di3}eBb{4sJu%NB;MTTqU*}zZZwcrZEs;989y0ki@ zB!QEr8Kym_BT#nW0=YoqB!NbSpb(8(P`$sZt+*(&q?Ai8b-L=^uy85%UaLVeYr|Ty z#YxbsE3Fd|hv^Pp{r>*)>cz!o8%o?i9$x=Aee+-5{6GKkcQ>z&RL0MqeKia_6eM8~ z2NHzS{&0A>zd24f8Sg*&)#Vre@M(HXGL>h)*>snVtJYH?0c2dm^-Em;R4_)X&%7+Y z_tIl4XI@|})@WUs&Y1nUF4A9A)cl%ASbY7p1{6P}p`W5kcI|ZHfFrSrN8FCxC(p0$ zj{8GOw|9?|YV6_^1JEqhuK`!VJhgEn@&<oIq@`oUwWNn7`&r~v<x0+>87dYfBVamL zF*qhFGv`TpmNH4n#rX^&2)VlQ890@MlXIjZ6*v{<B$BmcsrE3Jl8do?%EAPd_E%E7 zqzb>#NxjN??a_}i`{SKHyeh{lntcA|ci;c;-4AbG-t(Te?qck&H(?i`ujoJus6%CP zeb)`K?<VN8Zsj(=`(|JA%ZIzif7D-p_3vI(i|1LXRHU_3%n^g-eYiM6=RFbDWnn>l zTPSr9jYd&#O=Yzv4f)#SC>x1&Ng`Pk?HAWy{-mPL>w-TBh(7TlmslT=wKCF{J1oVK z>%HHO!}IIQ{c)O))0|5>P6y|Vm+oTp)y1YYBxt&UaG}9?T3V24tCGhx>C+0x$ciSa z_?iEsXruzvtb8n#RkO%kbx(Cvo!7!8W&4ttU{(CirDZX*X|J49J&HkfT{tdM0lf*A z`G%GUG%w9lhY)1)$9H=B{ruA17UBQ+AHMnBzkJh&{_5g-yx48xmB-P6w{U3`&Zz7G z&}rBA*>8&;_W5T2=JD?C&D$UE`eEGk!{yhV8wZM3Q>ST-=C$g4J{3e65lOH*K-f&9 zt`!}vUJ+oeVOzRRQF9j!{mVz;?O#Z+PD0fV{3rsjSf{hgUnHZ7f^c2pu1?|L>G^KV zFRqaJ?d`*UIwqDZAmk{9x{xYDLb1pWIx=|zhpX>j>{RA;YpYvmU0$roTXiVWvL)bv ztF?iwI13f4@E6gXRUDH`;6R)fao%Gqy5}NQuyRQ$&2yTjd7g3=tW@owRMD%SshGay zsEa8B1`&@gs~6Gffk6&b-rl_27m!y!JfwT-FMH%ZZgyS2^BSr;X}u4N17R=Xs+9vT zWR<Th>~zM&>D&MOwq*Iu;nUCm-DQYQiC77Q8M8nJWz_1-N-1EP$qHENvf_me*9c%$ zahWqBjVJBM)nPJTYqbZ;C-UJheE26P1K(o_e{eJ$xp;!4r;a==7AF-r`98R-?O0(` zb+dH;>dk)2N&#bZeJ~~zIlFf+mN~0K(|Rv1u~1za0JeGsJQcB6=pGmQ+bE>gganGz z2QzZ1^C48oIdCYxLT~35O<UzAL&+s46RD}BnNu}~()1Ho`%`DAE)-^6{woH!)NGTe z>>u)BKON>NNkQ*J>_xn~E(~Kg4x}C!T_H7cvKH$Y#^Th~XBwjRgN4ox)5ELZ-90`$ z^h3OUaUF)J#Ew9*nrB3-gF;&B_S;pcl}6e4vE{doI8yCqGkI&ywILf)RsE2d^o#n5 zA9{r!!S<(@S9WsCJG7FhB@tsfCOaQT-&yQXr@6d+*dOw-R6i`yVXng<!GMHy?eu^S zf>CdXT4A*_uaKJsGJm1vF&5847LvT-4F+Qmuu3sVQWTPKH5aHPj3pJQ=QF2^3o=8g zDl6x?l$2AdsJ&E}rLCyVK#bj%HgbziDOZmYid6!U!!FJ9Jn>v|xAD$LAtP#a!Hu0$ zhYXIL)TtLvELu!Bh}404k6oScTtwpjhsU(%`(Gb#zdHJ1*TVoK)*BZL!66A!YeG>a zI$MPgTF(^=!#lKeQVY$abCxqO17Lae#lC~A>7Ty<(aWDn53YFYC$i3GpLyTuZ2;aN zJOBqD6}rov?x*tnI2{=eIUf#3_0ClTsNPke5Wzb^Z!a~X$&x!<{ba36TxD@S31s2g ztc>>-ueMqSGqKE!TB<;@6Q-PzoVn`+P=zmpWR=X-_Ek$)%;4pmxfHXw7A`j`f-J>H zRjRm7U#_!_#urt}MWKo>i2*U9>UU^#IC=-Z{$@g91z)W(qL@F@Xb@z9J#i9U3X<K# z>E;jnw_o0O7cTU@vwXfMr=kmenOdhx@y);BRyfv5V|i%=TlG~$a9SUyAgkypH2Boh z>lZyf)L0fscu%y}Al4cjhqdovtGvhRrilqw)gP3J+|bAC%Zo2^nVG(SdwZB?oMW9C z)KIlKddyISyk}2Z6+Fed5J7|+4a-9FV);QV*0flK5F=0kaTRWOa#1N7#?XX{POK@a zmI4K;P_aq87$bcKO)9h1dq|TP!&Ajd!7LKVYUJv=-jy4JPZzVe^mR~{qTIScbIJk} zb{#Qr#=>5MuK=rlikOK7i&c|wHHup5iK@Sez3T-z`HKW@f0KUr*EijB+P(-~Pvp^& z)o-wxpWLEtZprF^tD3kvf@0n7OjhgC>gr`z4OYM8u-eQ8#6y8o`-oq*%c=j^@ZzaB zN#VVAC}$tlrAS<i%T!?{7N~dy1bysdce!&(^jKg&=i~iD&Sjd5l^R&7ix4};C@ev$ zIdta!IV&f%p;_!KTYnZ*#|FII;u-;A^weYZr>gz2<d|TcQz;~=`4`m0Q&V-gbXG|w z8BKDaWG$r?Mp)BOQ7*R98CC3IlS9@iyXw8dRd<MKGLU+!oKbShsc@A<>&!~5nU&XU zU8@I?#$GNg4l3*mH1t(+aC+eHzxi?ZMf~D7*ZpM&#e)k7on4TH`x+^yF(8mkI(Y#F zpleNHHR73bT@6<Ch-L}IE_BsazqnWUR9JZutW%l0o>lyP#8b8yo)Wd%CZxvnV<mwQ z{owuct6dU$d-r%#ZK6(@tMe^I)uXcdI<ja1t;7rZbc)rH17k9QFfu!f$}em}&6=U= zI0RDhA$yuhGbv-8b73Gz<|Rl=0M(DdtXcq)g(WPdQ=yYGqy0eZbt2Fr40Y{S;I0d# zw`zLAs-{ZShH5c-T`2gHt$;+BtB*+?Gp3YMPW9Vrb`V>q2FXRVfjC<nPDs$t_lKD# zIVc|#CI{#t^p3O|Uyr!vXVK~#tHL7;J_vB;B!$T{s58o{f+h9Y{3&nVVqGZfJpPYs z%JOrMpVC-seHNY<RW9`;dg5g0QeGoq1a1Ox3yVXXk&+<#t4)8k-CP`woU|}b2d)11 zoOCWam>Z4OYS>Gv-ebk`Mu8Au@D)D!RF#XF1kvh1YqhqZi8KPhf%Fh$j!M}SiIu&0 zMXZ0dNfE_V?VMzVtX80-)eeGIbL9#zg<2*>^<##*tIx?;t!mMQVr*boxoUy>5P_;d zvON;ZG0!|BlURtQs<)?pYuYwAXvVd*^|nl)q<uQ3$K$&)-w0nu&jF$b7povutYx*2 z00tJUPeEO7;2aZ%qO)*rc@ac$mPK5dA3|cH|Cg(;_GgV1pDqE<rApsxA1P}>p+$b{ zn2WlKJR6VFwhzcj9eBh*wA&23&34aoo~p$_;M`#bIjRQIMsZ`{0e!G6kHu`NOi{6} z4CaM90nCJEsxo9QDKcRx2Y|_Gi9kuJPBFjAOLo;oxBMb5ap%?trs~udwX%&5noVrM zQL8y+kj#?IDrSai)UuYP<v3I=Bc{<7wo1k5<g}_EP0FQIv}SB%wTLD^#g$v*5LRr2 zW+P(>=A?7d=^pY!D0dx+r#==4Q<<l7%&Ec)MC^xtvx#Bw6`-?%6BpRIu3(btvLqnw zV_<8Rqbem;S2qx~ki^*r3%{gbJ(b7kDY1kf7Lmf)CJ$?HUQh9cI!6{Yz+g3*7I!It z&ZBPo?s6ROk8nJck}AZU3%ddpykk+UkU4dJh7f{B&|JN7C996&)~ZImQ*s=Uov3Cg z%n(YV1LlNBDk-Y-!ns(!4GXAOSz>LV&YCJ{OMjS`UVT0b!CDBham(rjqx~xtap!_L zW3mW@?Ms0zsaI7JH??Pmu3FbA%dxO8g;KSxiLosC)ir`&1<mwfvEmA?B4=i@E<Nau z_ww%hiExVB0HI7WJx=@m@nM=Cjah`-?QnH9Zg+!^h(Y7V#|!7jNTCCrOUdA?!YAjd zU{@ccnC*yGB8%`preF1+D_FP``JEG=mVU#h7R2=fRvU*;)WuVQaw}o7P;dz9(D*0T zIvKs<5NOxO-cO+#sJe2?%yR`$Z1gKi1v-`)FjjwFrfJT(pgp$;xCV_59BGFdMNE+q z1!jL_DRA_XhN`h#PE4Xeq{K?ZZ4;lx%{8M^A62jn{#I>O|Ak`m23oADw!SS`7;=GJ z5w-*bE4d}fz%!HyCI%sJC6T9GHOqd=IAE;4SeXS2LQyCTrC8ZSu^f5>Z>^UI3uaLc z(Qm_+Cw%km<HL>4k`L){;6vgm=Q-t>wID(tsqdld+-4J=f4ckpH=jKFyXPZ}v=O93 zDMtk^b8<$ntze;bkp_Z>|Nj!(UikNMIX+>mrD5xN<bkRW4@(_aMefzDU94a0S4S4s zbGQ_RAy5}^=zJHPx2{5FOL#6)ycD6Nt}75lM+$?lE<ggx0C`T+Jf(S(tR&H6^oARt zVqS3MA_LAs2QPpey?Dq}#9Z~Z!;}{%=2}P#iwRqWM=6DM)nF{kzhFwmV2Q;=McQsB zAcX4b!>I5H_DSZVMSz!nry?b#<Ng@12ewuN6YG*<D#!(@tC<^_LRvV(5=>QmfkOy= z-{%AW_=k6j4=-Q8d3$#=)t_Kp+glh?EC4e?*=)n#{rb~?{NMPiTX^v+x%jN_w$b+^ zv{+tl+PHXUyHM#uZvLZXqyO^->q)AXXNsp@$!qf<UCVpd8o`y7wNyLE67Ok66F`DU z5ux+c2a2SzsxZ!6I8`u$84^=gKMO_l5>z|#!C@E4Wi5|$I_&p39Wt>6JuRJv5-OMl zjLcM=Pr=W=DwAqo3gc{PZ1PnIotM@_ugdvSf-DuLF)UO8d$A=ys@NIYF4k+jg$oF{ z`B)wZSko*i$;@apYenxI5lZ3dFhx98-KJEgLRLy>t^k8(u=snU>`5aoSlpF4SNB$2 znWUc0cab-v`7Q-3`g9gL4$fDRmPup|0{hqgU;oqn?c4mt>;0F%z53+q&G=>SBD4$9 zib>Qs5rGTqsY2=(ucz=INfo^>YAdqWWm}lGPYoI~vo5KYLu@KeTebyG{TYmOE&zp~ zI4Yf!ZlLk{N)&GIA0Fp9%amD#JW?UAal{a-1q*H*BSX;W86`-fdE)tar0VcPz@RKa zB4r93Q9NtOhP?~q*50W4pIjHRHMc?msji%waOoPoM=hEc*aN1Ltb|6mSU_WK-O<iR z++tZ`El$lt)Xvpcr{ghTtXL33nG1@A;#!j4+N4x?rnID>T9BWM1)7j-OF0rZA7=~Y z#po~^JKs@70Ks{X2-2J8<N0y=*PEB+pN_wJ{n^9q3oT#$-Nkd~2h!q#^)Mr>juW^g z<4Is4A%L@XRKJ*D;fG>$X8)Gvz(z5!iD7GRvG!h+McEoz)AD#MMCKC+xBgfaWI%Q5 zi1M=+S6_VcDY^b%e)sM4@>QDWZ1$FOHDciqx-krWH}<>1cUw2$h;;PRU3q(WyuID$ zM=QCmBSc;xD0)C*szVt9yV+T+9n2>vRb_~S;4CR?3FmrwEsNd&HdlznDC|&`igKYP zDXvyvTG5qw5iCVh%ef0QFVcBxqRP3X<MD_}!FO%D4nU`bT8fnFC8A|Qp^23?Gx*dV zUZfbJl4+`*pb@#rDLeBuxR9@I5FmKVNBcoX7WeS|@t@~!H(k8A?w&#KE`tNMDo!S# z2Iwqoi)RJNzno3{s2%qcr*mx=b{2?OdkA4m)U7F7bw<vx_8+IJF3ZSBi#{5mTxREC zw+o+qa?y1=HeTJUo14QlrIhnAOVKdIaUR?SM(4ZG$DMOCdKac#Cdd!_N4YP=-XV0( z_Y?>0I*ibma#V;~W20(_EtONa5f;E%@o}|wObub#|6Ab70;b+}Tykd3tA(=20D15g zz-baLdA9i05^Qiy6-xy+=6gxu+%k=IUeWmq&SdSiWL41Ms~Z4Sa6CX3KqtYJn<y8~ zl9QwijtO1xjp3*$=sg9?F$+?3b%BB#AMbAd@ao4+7y6xuzIguk8y|YAU@%Lmo^{*) zkKp}y)-NVlr)IIIZs~Y#4Nuz`>Kc{M)>ws_U3n5ZLdVU0wDzPBs;3XZoP0N@su+eY zbVKL{ce#P#@1Jct`Q5kfn{Qv<KTgME)<WauC6+`&kVVF>?|kq?(fNQec0=rZHG6(^ z{myN!hG(N2Nkd9<<Rhn<G8S2`4?R>9$DAszB1Wtjit0RDR>y+ZJX6IpM)w4$4txDI z^3LU4fj0K3zwRV}+#UDFo1-4IS|{&mbQBfbltrP0egKaQ#kxVT)&D!7;G8#;=B(qA zTeeuOLR3na3eVsQd7lX<t;VvdPj0Sl)oSEgyH|`Po>_8hc!wT)QW&5lIKKJ)!+-j} z{<$B!-6z+68!Xi*Or?tc${Jc)-TcCWb#4e@k+H=&Tv`~P5lh>Bt{WgNJnV*KTLr1E zf@ovo)Y_C4mQo>*dhbGj5V-HL_g+4~L`${4{PE%KyT^2x6e!JIVPj4)U%=@44xCDd z!`0@+=XAuB-!UX43eHCtdt@giW!5=yo>gXbRfAZIGxJl~U^^3L!_uw)P4y5V+kR73 z0eWjQV&Tu6>QeB#%gyF$^Wuva&%gR)&+~u&um9zThwmg;m(&mp*%k^5>yfo3nTwNZ zbUk@z)vqx`?}H=fkb?2Djp!(xbMcPFgA?{u{+*Z~>thuLF<!qRerNqo$qL=9-hw=O zhfYPD#LMy7>(}4i<oi#5{p`hWuEWKZ<?}<~tjMidMBDf1mp}Z+gwfM++U8{yv2{DY z=4Ggw9JShST>7e8VYsLRYmrbG=FF+I?TXsY7KN~CG=a3~-LJoVF~n}$y(Rws-QB#u zyPv0SuE?uTxk92`%0RInhTUhEyZ-7S9NzI=xh<9p(rleOQ^_fnlqAiplMoUvbXDs! zj|QHrlb^G2YcEMC!2?kRX8|cVj1@8QbWG-~@zv*_eg5}f{OaHR`udCKw}*$9$Cvz1 z?6Y$aEEP>^z1M=T{{w}2p0RL8ZU`YpAAK0((8s~K3dD`uiET${U@qufp`yfCl$4OY zgXmoBK#IcxRT!;=2rOr*-da2>VO;_N>n#;8J|Ee$pJ%-J;r`X{-^E?rQtW*s%4`jP z9obrd_g(&91j2iI5+hiP8+8Rzx<=a<AIf&dUW!Ht$yy*%>oICvSd_N?v?I1Zvt==? zWw&Ib2ZO0GqAF)Y=a9OJA(RGkzyI#-%QyG8$7w$wQXW#3$ilqIJ!|x17vn`7KO=-N z?Q%$6E+TT!49Aito{MnR{t;V_1-Sl?DEO$2)LPVbgthR4Uy6vlZ)Ig5B;unR#_jHE z`~0)(uYUXWi@*Q;+2_y4=UcoB!@q1Kk{l(FgG^Q_RAo0-AI`I3xq2!eH{*8Gbwe12 zI7B~q7hA@XS^&_h8p$~;p(Z4+q`{c}Qqs_A)M)t#Rb7BT7bd2F!8l>AZMsq#H;o~P zR~c@9+<)_5zV9yk;qr6XdgqE|^$WLlZ|h!0%S8Vo5Z<4PYTxIsuG=`gw~ytCk-U^@ zJ#B-e)V4#BmSVLYezH<VGV7FNnz;*77)wPFu4?Iu8>Z;^^B3D~7q)%e_VIV$zj}3h zmmk>Lj);_!#;hCSNIUPk7h}KK$)_^E&HH!L!`*(K=8~j{Py~<BWpEiVRYe3wxu{|T z;!B+qmQrO)!7BuyU}ElCa1OW&{`#}sm;dnj7r*-alfV0P{NgHZqXaF-0#V{fB~h8O zSHE^7H!UK>IR~PlA9kC~X4q`{&88cBE3L<h_0URV+rI2=-$q@2*81Uk3l!Fs>Ipi* zpem(U;6IfDZ1%vl!jsk#ec4hu_<n$$ca-ks<?r5n_Uq5S{4D|p^wGPD*Q)zTpikRc z|8j>XIQ0m6-zMVu#<+U6C)_D2Z`8}R53sR47m`1>#$uL=opY`hs9;KZJeK`F?GMv& z+ea*<Sw*`TH(j^uqhkp~eIItOx|er{!<-&(c%CNNu_l_kBBNe)zTb`ArgNR=fY~!v ztWsR`ZbmwSpOt2{UB|c;+XeIpTA>}dBDrmm5r_h#E8-RV*p0hkd%bz~>BZIOo9n;Z zZC{MTi!DCuAPADgEq?i{PyYA+<Nx=~e}47D?_LKQgYDx|hb~tquq58@hUZt$t~T4T z8%$kwedx`gRinv^Q8U_Bl@Z%q2i(%L(vWFv8)^G$WkoRK;@7Ut@<Fui(r3BGIwj6p zX8<}q+}}L#&BMF>@iA>v?>cX#J=0m8%r6h#&wqJMg16V6cJE)>(YDaKg-TEVTV+s} zZaLI;MBxgxASK@Kj}Lbbxk78*OM3O{rX#N@ee!JFc0koy0lF>(xx7H<ch8<b`~1_- z{@cI4{PyMBxA%v6N)PFnkGK!hJ{});`^)j-a=40@!`1e(e=+WFkB@H;>1`@^vr!Tt zT5qZfb_Jkd%QibjqOK1es8}o`7-OQ4>Nsw`{Q8Tp|KZDLUtRi3M_YzIqZi-_e8t<l zZT$P+d@U32-~9VO{L2q&L3dwVaIx`eqU)|V&z@hr*bHMt--p<R7_H-tF<b>XSFtG8 z-ml_X+mFRmewHj2`K)drFTLy>ctUWVd~}(zF2%%JxKx*@@nQk%f>qWGsaSX4Bb1|Z z1}aW{VM95s+;yGLvu*}I_c%$%Pxl4-qk@I{QP;kuZuNZkWIRhbE!2mnP2+IYZ>?9b zaY0qBJ4Vja@i<LKU@e)C`=g|+iGkUZhG(0>dsW~(qv&?@W8bCRZ{jxSxOEr*%&&iV zdoxdoAGP!;_p{`zr4Z6~fMIaK;SeKr$bH8b-F!Dqw<+Dul9`>LZ><g*c4q{wK7X~D zu5-}?`q(*g+s(M?w_WT*i0G^wkD*L6gL*;NyMXlU@^b#_tFOO)`R$AET<UyrT7}L_ zmgsReZm%z&ZN`gg{s5y3A#}0peeg)+g|iZul5=YaMr1r_GF}K(jm2txx!P9RR)dO! zE_x)WAdyv4&beeZ(oNfX#R$xrDvqkyHH=XbmRWgL%!(1zpKt5;o}&13Wb0(I{+utJ zb@W?_6=wvRry3o7>au`OJC3%>tj14iRo$l4>w1C$YF*9Jlu}OF!pr61et)>z^RalU znle!E&o2VGGAH3wXT_ZNt0C@w^$*wGCvF;W@7~<qr9*)Y0@EbNd&QFZad_<G7+mOl zKfLJNw%4hj?)v*5AKvoA-NRjG4&r+ZJ$dJWbKyD9nR~07r>GR7>$-6mI~?YH{^8pn z-`&5`USvobX6OrU-0o_;cz!XAn=XtmF0X#`^_O@5|9`xDllE^pBTBANaktxka{bB0 zZWqXV@}2Lhy6R&bde=E{76oTC%ilVZlQKdnCPb&gbM)2qu>C&3qUJ4r<H!Y%!d57< z<jt-1t7|R32_09Z@1@=#lu3A2vK~(k+m=AG_7m0*3TJ+vn0%n&er~D0x7rS)akgKU z_xtQEmvP!}N%7RB$u@>)7+<xCS1VDbf;g3ONb~+URghHDZs@!4*>2bz_WS+g@tbdc zoM(w)L>#YnzB4G>M~A8bG&maK;4bOz;@Lxdn5O-^`-2=oCZD>($K#>T-MH_EZW!am zb~_GZ+znyd`3;WSzKj=mjmOtBzbm?z`Tm&a>{tW3q3f>VCc18jL)Y~&#I7I6`7-O{ ztH=HKw~ytngxL=g2JnNwesTHY^XI$E-PQFo&V1PofB&n$dr7yi%l$;knZgjSFE5@y zzuFEXiSs`6<EHP2*oU}@*bz9WwyIE0NshL6njkZ@Mw+cLE~#s6p^Qt&t%yiX$66(m z?Yk4&96({VZcdF|YKDW+QOLQZBj+QRxwO7ft?T02k3)VQHTfq$bJijc@n_amFI)FE zdI_$f_LF$)S&tcg+Ufm_>nBz=*EV%WXLQgyXmgfh;^X1Ce|!Wk&##~P(Cg=)-rqg^ z{`cSg_~UD49s5nB0oZ-<d>0(E6cPwzQKEUGDRo)4;)m?rgUD3UK5-y_MSe?=S<+PI zNy<Fu#~Av)+r@G0`tV|Ny}2It&!_2!{oxO{cel4#%7q_yyWMqnbrm+7Znqh>!3B$n z)FDsIgfR3-+E2XC(>_nL-s8J>kGKDN7<c{d@^wG<401hQrVGuF@}QGsiaPAJ!!Sf2 zy%uY`Gjws&$03GIq`r=9VF$S`ITmq=*>UR-xpePdA~-}9kcm_uv6@Y(4y-9P6>#V} zj4^6~GE2z|!K*@iFGhc`mKUuJlh%6?EDFVTO#jIM=^xWrA4`n>QF;3}1nYhE_F=*7 zs<&h*>U;0!nrNvsLWorlSP?|g3RaI(+V79)I7Jsey}sORFVNB5ZcOu>Qh9uwzWc)~ zCx}^lahE$E`(R8_grtxd58Nd;mOf4n?yJy=FC|bY;-<tChYhAdN}i_4g+31BJdT$` z82sS+=OOqCuaAy48ssjGIbQgStL}1(<0fp!ek2!*fNj~t<iTM8@WO$)!X8dub9qR| z$HN0tzPR3AKD&7K^@~*4DP3$gLmafAFMS_6XC0Qvc|SxqcxsQ{S&&YVh152#!gWIw zU|pllL`}-Xa@9uGvABb!s#&-YTBIR@Ro|c#DA^otmDX6xI?%Bq`-sj%^;^^u|5&iK z)?K2La@zg>|3hK)&o+f{*8lC)ok&dWuKVtt#LKefi(Xp_cyMjPhAq-k^{+2k^Ca_O zPE!Jwadgiw#%Ir-q4(RapAU1M`P=lvyEm_hO9wP4xfhqa>kA*E%Mm6AllK$%soRu3 z?1SEdcyMD-^N~2I8lEUysjnQB`ssM!dB>?6d)N2WciZj)uP|;WJn-Ae?P-u<>n}FY z2kg{)F<v5yLv~J}?KtN_qlyOt>X5ki+b#~XOnNAfddT?|+82iqpN$(*&$E-#(%KsO z=!b}%gT6jAIB9K^+eqTVwQWVsKupdyKrsSH;ZmIv%UV(^sUtGE;tJ5JHuDrblTwDd z7IJoop6aqrWlm+nATB`YkOEY53v5yM@>4V{U1bYD&=Y@Bo>~pf&)wy~Kluu0^yH^4 zsg@x7S`|i(Itr?%y}!Up1)U|)u!yS)EKxej^qBILBe~0AxbFL_zC#z{1<4m*5<5-# z58wR${`Kt-(si@<dq2)wiGarp2Z19TZ}RcSoZpJu`!0`81I@!E!V{OH=$_n$WiPmO zWdIt4$C;0hb6>_TZK9xs+`V^~+l!J0QjEFp-KL}HFd{h!<YM(np$KM{S!E(pA0gJs zpn$FfE+NKQk9&SR-0vSAZ{rZ-*k4|bm&2upc+_&_T(LP|tiGV9&QY*U2;5SLt!f)N zE#=jXpCtf!X;W)kr`a|JMVBm@F6*{hLMz=MrAW4WY}<mdYN}F7)q)^dSWsi<eW1|! z5QFndSgg#Yp!Yvz%sz-6eAu(<>_viB`tfIW*lM(ywm(&ChTr;8&}lPDT=q6zH#7xV z+htqw9c}+FYrjy%OX@nA_ho-KKi*8`F>hjj88@9`WFb)JJY8N<Avxr7d%XGa{_)4- z4~P5iVb>on?x5s5&NrFwVZJZ-Z|D2lS?}E?UYCOD-g@|S#JNBgoPj2S1Lz2{fqY~+ z<~%|PQhH#7obx!jUEX+%j$E@+T^NH=ow0KoiB+eRV5;6fv=pFNoes3#dew(aiFNkC zQN2hq;$W%r=P|Oi5EWxGgy1acOc<@4+cjWn5s${RXKVB(C37-Hm&FdawS-h_V1dTr z(bh%ZqZchzZsyGO2ZYdduHzt@C6$u7s&9049b`$NH*YjXa=cJwELOh+hR!|vKWYj; zE^YnDkwpDDVSI13x_a8Lc){n(*0j3%07i3Ms8)Dt{0GtqB26p%tX>%A<2*g`bXU@Y zMD0G^J^$kB#bvi0DF9GL4V%$J(3$Zd-@%)=KRg^><?n9Z@((m%$M>3VOSzG9ka7em z)WPt~#l(Ez*$dC9B*r`!*$Y2t8IVTQ9pVOP1R9G3&R&WW6?W41$AOX$*mq$Q-KHCM zajY0fZOhdnER#h5)M+6~Mh~?T#v)~5C?iFTS^=a7fwI@xrOD5o2cDRC!4j?4gLNyk z3TV}(YQuO|C2`KyF@j4yH#8=+uG^wWL)mTd*7wy)Wr2+P!`6C2$y0PO`{JDAwrw4- zBXH4mSA!pX=mSM(S0h&G<YjTkKGI1aZj-Mcicw3ifGpVOBU${@IY*zcDdfggM_l&P z6<LC{*jm6CQ@owZFS|2UpVwoxGQEQc7__)~%K8X!li?<GkK;IWUvGZ><@VVQqfeCV ztkv1?-0=DCLa(pz<?rV2@4h=c{xE<4{mtmMTiV9F?WI!!8l4oi#x3(yjvcvUNsmQ? z$gD1dhy&P*4#-F1u|TiB4;VcSq+M00jlN3kJJ*l>cGF*s<JR?r14%$}f~8pTA<3*t znT;5?RBUOsP7hT`g0uXf&~%)b`C;0-+4o&vhbIK|B$`v6s03d!7T12;8eB;}PU$%3 zV=8mbsYu~<lN4%YbB&F^7C@o#T(wc4txVN{b&XKAxK;hfN=Vf9M+V50E}oB{{q}Nv zK0q((vSK1vhuZs)xz$$DdkRV4S3*x6VdO(P@e(PZvyZ<=l=!29HQRoIt@|LfUAWe? zA1=MUEEwIIYY4ikc_V>$5Rrq<UA03TwV)m~-3#wM-#Y)c-yE)Xu<0Kz<GAx2s^6qc zA$S?1kDcR0@TNR=d2l)Ff#(9%j*Md;b{+LNP%9uUWu}ajTSJjL@e`xVIx7|<R7rhS zB|`#W^mCwi5otSmKROB8ngCV-@L>!(5Si~LzS?a{S+jWJYIo2pQnCb~bzg9eLcrRp zkDN<Fv(72=vE(Ujx7#rGJq(^avXg`^cPS<C)f;F{R;5Uq<}yuXo+Vj<kL{c<YvNTb z93`qwmo`ySgqgu=lAw;Zh*l671Y6S@AkVsVw-t~~b9b@Zy%>k9&X0thvSTC`6f#P^ zi4L9Vsx=D#VVC{3yKoa7I(v&&ii`eyU()juo~oV;oh5~ERd)^36FFAvI%xg*Y|F_3 za=<x4>L3NfRr&E<y1RQkJWM#zprb3@9Ut(mUR-x^jIIxH43GkNS7x5x$^P|xe4F@4 zn|Qh1T#tUY@w*PkjT;;~$x`N=5?Bi}aZn|*YALG-Rih}Ch6RrZnZ#jsYT!ErKlc4D zs*&z62H!a!T&&<4qJ#P>5ow)4D%A?A1Ixgf@mqF9)fVUyFoKsrVG|2vsaLSaL)5~+ zPJAQ{h&WB+11tDYh@918ah_71<}}T%QKi9mT)LRZS_W)IXDUPtt)*bwyu2X}Z9OF{ z;j?g!+m^LtnQ5=P=bIN_J-_^PJM22>TW?9R9j$rs>h)tb=gHTTQ`ZDIDKb56`$s1u zXg-IWMG;O!y`K^+E0QobxV2J)=H;z@Le~OlLPyS5pvjTSNW6hL%F*-Q$pOnlK0fgM zO*-6`a<6zGr_p=wk72qkZ{x%9avCoBZaZu)LfmBXa=$;`et&#?oyr4ry6J}TVz}P= zi;dq5ZW!I*6w8!SU34skt&#&nk>GtOz6i(aC1tceL7afHch=iZyi(`37n@PXAR54h zFgh9#f?;KCt@6-%t(ByjQA)L0hJ9KojZw>%p6UiBt!=j&-N!p;fhWzWRFGbselFyC z4C1G(t`zBF?^^G!EP2kw9yujt76xI$Vx2Bp?=)F@All!+Qi>E~yER?5^!8A!%K<$W zrh4a`z_IvDonCx)@%e8*d-2(2=m}uf>Nk)DSe4vK`Op~vmsP6R>Ns19?un9)PdlzH z{^LSTsQ70Qy;h+0$8?yt;J>y`PzttR8zR<0S$0yYP77i6bHRIXUN^4n;Mnn_JKXBw zO`6{2bSLGAWkM}OpE_qvV_`ORUO~=$^J@PtJciD9ySq3JF2<Sp_VzKofu46R^udi| zxY%|V7yZRHjy*-mB?&?<8ObSm3qdluh@Q!-69UYlqyemJmF+GN6a%p9bKJqWfdRP$ zbdkE?oV6}1s2(I&(*jVxI7>d3dCK!#l5I?pt;2FF!f(;VIy|M6RKd3@dKZvfDy0<B zIp>P!Ljh6@nVe%tDW@)Z7h5@EnJaoP)@@$10AuS&XmSK9ByB^5Dsd-3pGE7v)jL~u zZFLpS#el&jYZo=igi^NK?&_=SFMspNm%sjG_eo%PKj(YjaSIGtT7lNyi*mlflWlUr z7Gjl?k;3<OsBGoRw5D?4i8l3K;QpsomM3RqVm6}nvY9cpPQ_sG(~J&61yy6<-lb6h zRY0o0j$^*T>5V+RDUUy<=_bpb9CsdiUvVsU%wo;1)l~YFaDSW*c@G4xKZLRKT}&YR zIZL8#c(&PG$1wD9vx%FFVLOB_DrT9*Wzr^9WW6Ot&@+~TPQY8(I)vu-6jnr6Z>Mt@ zuC(89=rd_X+xy057!q5|gbSlOuBHw7C~2RjL!OTnSyyZ=EnQ;G7GUY^z*XMZx}WLF z=wQ?e@6E7*;KV>))fEmt%>OTCZ^9!<a-@s7nI936Sy|QBOwUCwxtzEA4hRo|An5=9 z0s#`B18=q3$0a%Ubk~&;;fJ|kpLteS56Km`*=Wx6RCi@Yx|@Aw_SyHPeTq|bA&9F* z0XrBj4IL|I`fcd8>+6AzV5i!K^Sy>B9A?OjV3DXyJf#!}#b9oAuerMFXnXng%|HIv zUw!_Y*N0cZ9jdf*agSs~L@yWeQ|})MBMf_n4sGTq@}@ztCp)O81^~_${u7KJ=$AVZ zPygCKj#y)ld$bvb0rO~znjR5*bfkSdP@d{-%J1C!Z`S)iZIAC-d8F9V!dl70ZUkCG z=G5g|SHO_5Jn6ifG)XsV4^kfLj*{AQJjB!M`IBimF3Z*Ar#Ym=-j$~DrLn{33pho3 z$ea%pLoe7qRYdL>7UFHMde&U>lu~$+;=#Fu6QOA=?JYXCvr>iJ1U`Q6*~#9^*7Am? zkOCk@Gj)`}%?4E>!(}0OU#c|>^#2@I4x3<qFz;D}YVrFXW17=c(=u@kkb|?A0rg3J z2DP+Sh83oX&`7P^8rp|8@Bta7mugcUC+K{J+U2~hcXxN^{VY^-JHP(?#XtO)-@N(k zs`H~3h;t3DHCIGxN>J~HUH1nosLoFjYvjlApPv7w8*UdP>s_{IE*eDc!%X>kxT5C> zJs_1^M~Z^SUY(ea9#Ie6BW4q^mx;=u-N*B{^7vi3`?fy((DLdcEz5j5PS?pFd-3{- zM8!P4RHu(S7YX8c(He3VS8FX>*`*b)QL#@cEOUw_91e#iT%|MzN3LRS)<V<hgxUxV z4`g_T89ZV3rXOtzQA1g2Tg}``bm=t3#p*JR1R9U)p4-F>LoTg!14-vvrDfN)Ud~$L ziJjGEzFgL(6jKs0$rymq&P=VelJ^F6B#l}@u~@5FYLS+Gt5Zs?<yLFcJHY1AYwc&F z)sDs4W}h3rg_Yb|d+VuE1P|T7BDuk-*evq&TU7KlnEv7GuU~(){FndwyRUxpc6r$= zzi)0MzXvZGiqR??(rx<T`eB230pi^~V`yn8Y9FA~^M-=ESXPfOy(H6-6!|Z7v&bSA z)|KJx@3900aWFkso*jFaxF=VqzRt2vkKeZ+|M~Ip2WgKXwyVSOYMHL)X-Q%6oT$x; zj$4;p+qReUS#oQYT=U7z-T6{BsS;{1(r|;fZE^b}-3LF%aEO}Fxm8!P<lMKt?qwhz z+qi~9lli`r8C3)i=5h2{^Ghaom||kbHMd<Vo~izA`&xuG`nt-DFi=gRd+Tiz(R!_R zQ{x6uuX<2ghd|-Q#(FRkjAT4q7r-8<5L}Y(s(5y$j|9ykP6@8rL%CId^_pmFmr@6> z<RW)O8Y$7m!rx#uL~Ygi5K@HaJ!)A`QwZ*K^}FBw{XhPXzxvgG_~iJ)@wD=U{b$J( z#A78Y*9_9zeBo)1vppE_nss?-s?$?-+WveSaBTGT%b<^*Lo0F5>7O5^!qS+$W^axl zW>u_=Xp&-yEQ`y@os-<9{XL)m*dBi0wp%&Io9nCBScX5Qc`^GW*C$6o3+A<;V&B@{ z#lv|564?;&BSv~9@)g=rkNbVzTIeD57%EYkrS-Dr^}H&e&Tx9G)#TU<C~uC*K3T}1 zH*zw?P-;A{YnQ^IQ)sPc#bV7n4$^t=U25}F>0G<5j)`lPC5z<V_R<xM@3jFCVLxA5 z>2)|_^r3oWP8QYsq1^xx89k7DZCSsL162%~tF&DxW&~HVs>nWCqw$GcO#StXHMF92 zWlC_(xzeysLBgTO*>fa2&jh-BVn4@pbG$k%3->d`3>!c_WHKUgk0$ZP=xy3=)mX=H zrbuUJB=teFs}NW8Pu(mGS{gm2y^B9t|G*}dkEmMi(pJU41rIqTZ`w*Qo@FdZY95s2 zN|Zgdhp_$F&OgffqojIT;ykAphj>iW>{H?xJA3I^C4okvgf5}?=y>wJ1;`udEye~| zGb37v)-p9ICGWL`I1z<X+Uayd#OLjC+c!`?VGYpC^9_y>7eQ`uCY@HucOPh)J^6XM zO5L};?o5%qcVgZ&4wGJu6PjXJs_Ugw6_TRy13wk`faY8Em3C`&n`V}tN`bjchcdBC zEVf$3uJo2`&eVz~X^%>aou>2XT=AWPg9#@GMt9d?8hP1D!AgE>WQ~WW5P}G?3K*-i z_>f{$kOHr%x^Iu$`B756efRUX-z@3WULFo%j*_E!p$iBTtQln1ZwSvBfGZj9$Rm0_ zg(OReaX4|i_K|dl1ueZZ^zov8f7T593-jqPLCxQ!Q5p@JvG=Sutaz7$+d_Nf^<BCB zuB_j?rQ96C%OzgVaSkzQu^2-Sf!Mc7t?1FIqzFqR6i!+jQh84L2ge@0wU7+zwdGpw zety@Q)Pgxph-+T=ecy{B;wn8i2U2A<ZG78S6)3Zjt8+@H7l&8Vm456T^j>iGzAFmL zrI$X3<ixikg_^JwuFzNPT3sRxLS3|Wja?ZPCR&sYD`*-FG{)I?*PP{Hn9z5~6p^0M zmb0rhkgHzvtk#`AacCS8d7%({=+J6&CS}plzOp`$KBNvE6L&G~Mb(ILlmncMk%JzQ zQp;ZVRqF0o?tc2|_y1hxgPUJ|dRhX9#IiueWH*I*vWOxT|J2MX!#JgVWJ7~feoSmM zTs@hFwhspg;>G5A=bnZ<A7a&C>0^y|+q#&Wi~dXXKCu1em1U*<wm!Zuw?D4=y+$Y> zrx=$M4<UvYUGqdyGtT8TNNuImY}PczMz3HuqJz!Oksx}ts-STSskhSh{d{|;H{1I- zrLApQ(N)cDz@H)(SWGO<UYwM=6Uoit_~x^h@fd_8mwG<$)LZb?wcLt>uYd<ohByL? zmU1SDMx~fi@(?C1dfsZrT502ELtaHY4B!oE8l&#jTr>2FBEnA73J%jdqFP-@A(*og zS`Y%)6#<Ml9Zk77iK0m=mM#=!k!$SiD(WBbcpg54;C-!n1)F}`9@*dheEYcnbak3< zK9AGsI!)}t1m}T3&MSQHm8ogOb_I?Hoou(n9J;ZrIC>2|To&1_cif)X2)~?Ce*~~T zY{35c&@eb_i^2eQoMrRKhWLuhu0FQ)eSZ9D+aJW$#8Y%0bFC0u@Dw_GYILDKNbjY| zo*P!{O%St`T61a0CIW#WbhsV#klAC8C6}`02d&v6$9c+HFguu*7d==~imRs33<rt= zu{`~I$xEEm>CMf}+w0|OYSQ1oyS;yZ-tX%9?yf1!vdilgbW;-#;WL7m7F~4Tfxz@~ zOK}kPaA@&ZAD*ozu79K<xItt_ns|yKV42@s(LkTl-xjiEX$^fPIxcQX0R(uk`k+iH z#FuRwEDdO3hQ<TydxNMg2Okmy9g+yu-1Al>3&h~#di!wu!%r_?zj*b<Db4BB=X5>6 z%&FK;zkZCNxmfhhb7ie|w$4H<=z7|QHdGR++W-e}-b|Sp8g5Uy^5b#PQzHHAlfzJL z&sKks7HM4+ow(?Fa?RIsKi`(y?;jq1*m*lH{wi`Z>-9#aXpFNBkuO)hQ*bO^G(D6p zxAWfiYSy^$eK6_jhJL;yThg~intn;!*Ym?Y$2#+*mn-iX?d}HF(QvL*OkJ}6Y>JOc zEgb3e`ug*~`~3RNjbpmIz5VtN-~aRf^{?OE{+Q%&6Q`m@1W0$JiCUN{Q6)i8GCvAw zt&v99rie$xI?@E`8dJT7d4(P{*D1GZD{c;0@#zT<m3VR#8VP5u@}zX0G%LXS9Mp7? ztmuz|mfa40hTf>5=Pu0(R*VRI2y@UllzQX5*ZrKGXsKS#rFl1p!@KY9|K)%GR(AKv z-OKsKM4@%Jvv<p3S&lOwlbfOo(BEVarygfZ*N$jMYFFeFBSWH0MA*d7E<?&o7~^Mk z-!rKG*BQ=X3^P5LKY&4)0ckB)i`yQ{{kwAi^Onyp^@y;H5i&K`c8t`Z-VZVCx;xo& zTXTENeJefd4K!a<>(f|01WS<Wh9g}w^+cc+EQOaqp%X<=@JcROK{TX>oPNRX%1*5~ zF*#bT?Hp5>r|EF;$Ak0k;M#n9$6D|1`g!o)9nuucpKXgup&3b`iy|o4Ia(zG`Qcz; zklXE_9LffbX;vmd5R7YUTWxzQb@US~yKxh#rGW^^W)Vc123zdenL3Fk4+VV?Av8~o zj-D8prM&<zGp6Q|LUha}mkleL4oWA+);dhb_5J=&|HtnkcgV|iEPd;g<K(VRhwB$t z*RK!hn%v~#9BK02SIEi1f?b-)c6Dgiv|cVk9fK!xNtDkQ0&T3!e^bOVa+Nb_Q7kf} zQXyFW@w%4B`+eJ*#s%yw3sWOKN1BVaO3qWz>sO1^v$VCeHJ5X#n@ENRtO7T!JDCEQ zv?lD12dw`q#;gR{tviLi*){Has~rTDe#!TWvUA<Ge%`!wiJW+a18~FYK%fPltnBAB zr4+U3%yrF=rMn0=gJa$s+;cSIN~Mlt-$r_}C3v_hvS~~9;n`Gct!3wnH8%U;Q>Zns zwXAuU!8Gv^W_d+A5M%+RG;)}zhpIJgVK5c&<OQWS3|u;yE<Ax0<1|T9h-q5pFeOI2 zKks|V3R`(J3w)%>6)ktPi2k{L`~SX+3*DW6E^VC_KTqj!T3&x~b9@zVK0Ces?0UKO zE=}DeZz$^r3~NJuyXe-!<!><ZK+W@)UOfF`g7Y!k=C2K*nIJG^>={##2vAB7(!#^t z`R;bxRuKt}AOxkjwP>*1n|BadV`C)u`q;}_>YB@1Wp6Cp#dDWHFkSLq(SsD{au+IX zbJQF3gNN3-?HRNpd)G=+Axn|b8$~NAXW0uk@s728PxI`S;$-3}-{<w`HMl^D_if*9 z_nIZfs4+;gofhx)^%ng`jGuaxhMTH>%4mFJ_-tJz+yVF`z?Ps{p#(irxZ1SRbCyCu zp%{V?w7@a;070VZ+lMl)Xtb>Wb>@|N>G$;J@n^c#HFW8Yn`v=OggqO@pL)5gr?DT* z&P%1*T1aVfN!E1!N!I%C{g2;2oZqJiEx+h)J~_Pj;`H19{OhvzH(y>IUqPiy*e)Y6 z$U#UPgRsEfjpB3|0n-O!*1vD7Czyw>4BfuqiMm9RiP}<DzJFKW|G1ZP2rURSn@TC& zv4ZKyq57~hJ5SB`;^ge|UfYiGNVUcVW-nUWNCj7%I~0T{qZ}%}GyG^8I+M9op|*u- z3N8VYpbg$yEf7dAdRS*aUmuRihd2cazPUd0)$O+rdHqwGW2L(1?cwp^_Q!K?fqlR( zyX+9Ifi41rIKbdUVd22cRAI|BH~}MQBgBDLp!b49ZL1~6+Vi@fDN#Jk9z*)FOOeu~ zb4YF|PHWWLilRi19U$()w%n|V!XvZjIpNXNs@V5Mykrd*ra@n&Zs)D8TPqbi(0VF8 zM_clu!m`Es;JcqDN>>_!V^j>)>Xy&Bm4`do|8)Dq{cry9lP~|n>(kpvR}_z)eRLSE zOUnfN*1?1sBOUsfZfain{~n@WzX)PkCZz6rkFG_jsqTG!kn??WTMXShPuOVNOGQP~ zycDh;kG^_exo4)<eDCZLMd3GtZ6t!Dmems=t7;u$fdjQku2`b0ntZ5fz1&RCdU)#U zNpK;?=@8Q?PN!v=;{=*C3w7_)`F(l3zss%Eo>55FoNKSqHN=*VzqeCG!01%<6rmOx zLpnDI{(g8n8EHsxH!7Jgljqu@!{PES=iNPi`Q_)o{SSY?91cJH_|t#?!ymu-=DYRr zoM?`5rl1A96lrbK2-zjDX_0BYhJ`@0yM{h4q=TPW2$%x{>}g@F_;lZP%%ct9325Z( z$+7it1XTE*y6a20af!7u={3ui?;jqv{e1rRhi|?w05o6qH!prYxzw?u-4q{oL1k`5 zn2BODjokI5*CD^8fIel$zrysx!MgbHVq6fqi(ONp&s)u#ThDbWxV=s5VX2iv4RK;k z8!V8orq;Q~;57C_QdIz9deah&VciD)r)XV*7zWBJTj?bVuG>EJePO-cYHc}}X1UON zPBF~a%kk!Lbu%Akg|(V1i|C0XMNei<UNkrNUFzn05g60wy2A{DdSnf?kWsWHtBcyD zyV%W8H<xZ>I7+XjNiNn)B(LzgkmSDaZmqGr{mrNU>3{lqKFr78r_J5nK7PN|5`&*( z)DYczrslFVcbT+#80O)-nNvTR8w(zrn3bGy5VbOtEoZMdy_Z_`y2BJ&nvE%&z%Nq) zLfLaEp%i_l`X4>qwV6xwZJs#n5BYB2e;>YIw{v5eZ@!9Ya$dxDlO$%#zgoj#Og@-f z^laoz|6YCE@HC$$W1X7Rgwv8`Ut23$fswunS8@fBTSDj_m78G@pj(CBA;NkvurXfH zY+?ZPCaxQ<fdxL$2yyg5QtGa{o<ye3O|xv|&Xh|=NvNqM#%Z39r{#EkJY65I7R{7# zw2gZw%xoA*jmYoNMx3~_C-S9bi~^jr0H+D+)tOAJ`L$x~FAAN5V}{E<K~3Y*NrWe~ zk+X>lm5uW%YYSVvqT{E_%U_*dyt?+M+S4{)xqtnaKfM3xVSRY$GQ(1lDZqM9E_+jO zYvPNJG1{#*aB7z3(3OY|cB;7)3>gV*xDdvpCJR>p5jM`C*7nvD))BN&GB20lV+_89 z*2+UI<@f*Xn?L>74=<KCU*5#|DiCXIO&!W^a5caL*Y=go+vcL0ZKsMyqw;UdhCx0| ztTA@ibil6jwoBPtEpoA4>qUXKo1!Nns)*jqFhK|!i^G&Oj<Anf*ffn;%$X<`6VIn& zBJdR}b=`J;z|{#ZrH=VZ>|V%ey2+*O)j5tnE%WhodU11gby^O~bVyhu*Kkc;&lh=y zUXrwI9$PR|a-OvSD87+vT2$3qSOgfWQ+tw?L(qT~ub~S_qI_V+u~vHMjZ33SQ!h2I zedDE_K0W%Ez8~81%D+iBE`EOfA{|fZ|NP(n+uiMBE;aEKC>_#~ISFvSk^2-w1n>2O z)o)r$?!7WL2^*9!JXC3&YZVQ@?kGYY*N|I`!J5%P+{UEnRV(nq-udWaqm~st#%T&+ zDpID3DTU<q?*6;qzyISu{SX)TIvpo8>VuXCdo%YRP0JQm(Ia){aPxe_=WpH350@E` z3<<)akngM)u7DSQS<>5{_e64t$LNEn;3;{I-X|!$YJESY$ibtNSxKlj?{{_^X<c0! z6<CIv6uhagH~lm{`OZGZsL8Q&5sfLma|mgQ^Sqo6r<<$O=`bIrX$q5~b}gAg2*A|N zxl)STyKH%s(%ebJsvobwcyF>7DVuAJoe%!mRwe|W^$*xaE>4)Kb%4(6u+I!D#8O)o z*)wlF$>Gg>^Z8A<4tv;kpAWB=Pt&Kz<6&Nw>#OUpKKu6m=d#}I`?+?BPGS!<1t|rS zM%EYJIy}L};GbSKi@Cl-(vL1`4tmmABec0YcEZ>YFHp->-|Aju@G*MObkP&Lv(}`@ zJi)n^!bHx4H`v~Pv;E8e{r&OP;l(E}+)=CI9^gZ*e`SURH3hakFm$b`(=Yd=|E9AQ zK#L}fNa5;ZCTte)&@dH+)g?=IrN<cc6orJYP>5j;^Bks_rZ{0}LF}=Ch(OQ`wKFxQ zOjO9hlG4cDDEH7fb(T;$uy4{bOzfw)_z>fKSdQ1r@p@hkDJI4csZT+V@-*u?)+AhM zBeX!H#udGG8g;NkRTKsqyV>EB(W<{Z1uG(t1|<ddjzVXYYRH0ukBZTmt}X9v%PzZ~ z-9Zko4mV#OPhVc=rTo0Vo44@tbaQxdnB#nPxc=4KFW>*>{+r*w`{uv@@ckcuzJ0gm zy_1hi2z?%Huu{t7`ElPi(Gt$v5*Vn@`ayVXABfEx%mI=v1cUB;&?xR8qZ(Xud&t{k zlU7>cuFuf13I5bvid1aK=!4^#4~^yahx70M+jn1m{drvzwa`jyeT!^TA{YWF9%vUi zy$>W(XLe#v#`OCC_h7YNQBPx%HvAwp#+p>QqRh5$CAJ<WigB8JN+F~WCX-nY^OBBJ zSiGNu7J!P-jg?Y6bF0)CEoE=Zug?TV8eDwkGTFB#V<VW4)8b>A=d>KstOqCt_Q{7i z#p4p@l;$Z-F@@;#@VG|Biw`X{5#J=hmV#aJ&TDe;DG8Ncp&nUmgZ*NR(AYFd8PicV z8)h?dHeaA#H71<bdTzcxuJ`e3nwN=BAza7o^Skvf+-my%<PfK{EXR3njbF@eJ@{?* z+Y;*Sy==A7fN5lKrUc?C`i`{~t^1md17KLJ8Ss*h`(+*7hQ#A#dwuxq13OL&bk`i# zh??{IG<}Hdhp8~Lw{C%_H2dQ@m%H!Rci*0Gf7s^Jk!}+EgY?}|)q9W60ztH+?flFy z<pXQ^Z%u{WUhos2WvteD@=+gqbEWqhnF8Wc(8v*e1XVUgKTZCS!XcP8p+@Usd=vN$ z_Kuw@v92Ea8H$ZD7z6R8ViVSZ%O_QX2b@-W2r)$aK}P3u%_WM5IG+w_PPU8P&7Zd- z=Mi6SRUkbv2@XdRVBt-5t-I6v=|K>-IYqbK00Qu_Wj`i2_O2KZXIRT=NvLIgoTHy& zC(J_k?ctnKT&dl=`*8Bj=TP65`q+{`ytv}^*vm1<dVlwLuB{QfqLs`py&dN1>S$c; z`gneHtzFjt#-1=qheNGXc)<`D0lTVJQ}p8y=Cs7<S|usHbZ>LDh8@r;PMTF&%&P~p zGevFAxg@nd+}EGJznfq5t9eSZ7Wi7inp+Ht7W77WG)h05mZxsk-*B*+6igD>{#F~e z6ALGqk3^wvWtElU)r<MCEXSkf6>=06HSnBx@`PngiEb!<Hrtp2u&kEuT5Y*W2CAs` zdrvZMdc4LWlnvHwO^Am743oPYm=3WOpO_<G%=HN;&X$L3Ek?0H#avs>Rl4(>P~wLm zzBGGmwkc?fl&$NBq~8oUqmeC3NoB}eI%)G14~OaPuiw7;`t82tKdj$8^!NMT9`Dwc z-G1M0!{c4tPIT`x)4ikByDioSX}j|+CQ6g<)XKJjXf2hf`4Fa*q|;-~M#W^P`@tDS z@1ACbz(L~%M>GagNWEzxWG|ZF@9HOieY&Yswzd@zM^JEyW_7VP3cMXUl*zr<i`C-# zP~QFYaD5k#Zx`kX?zG)a!E-eXOgN9^E*+q5v{D&d<o-dC{nE$!WqUq&s^B&S=aO@J z#93?J>Zcr!rN_OvQfeko(=vxN!CQiZ_dW$lB*``3txoTzlWEnZNG<3C)mrMF>sld= zQ7}X?+hik*P<I#UNyg`m@}W8o-5l98Low+K^xYg#s}xZ5BSG6I_g-475KAn1>#c#r zrRNwjo)nl9hs8%~u`~^-@Kq{+FJjCEpphDENy%Z}gIzo(I!yEF<<)=u`XBz`fBw%8 zywd;kzy8y=x!kwA$Nb>yEw5aC%0DeNB?(iW!XDl3Is3M{ws&87hHz5Vz*`;sLjzL^ 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 QPXGV_07*qoM6N<$f~<5rQUCw| literal 0 HcmV?d00001 diff --git a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx index 4db201236..5c4b0b2f0 100644 --- a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx @@ -21,6 +21,7 @@ import GroqLogo from "@/media/llmprovider/groq.png"; import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png"; import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png"; import CohereLogo from "@/media/llmprovider/cohere.png"; +import LiteLLMLogo from "@/media/llmprovider/litellm.png"; import PreLoader from "@/components/Preloader"; import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions"; @@ -38,12 +39,13 @@ import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions"; import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions"; import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions"; import CohereAiOptions from "@/components/LLMSelection/CohereAiOptions"; +import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions"; +import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions"; +import LiteLLMOptions from "@/components/LLMSelection/LiteLLMOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; import CTAButton from "@/components/lib/CTAButton"; -import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions"; -import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions"; export const AVAILABLE_LLM_PROVIDERS = [ { @@ -186,6 +188,14 @@ export const AVAILABLE_LLM_PROVIDERS = [ description: "Run Cohere's powerful Command models.", requiredConfig: ["CohereApiKey"], }, + { + name: "LiteLLM", + value: "litellm", + logo: LiteLLMLogo, + options: (settings) => <LiteLLMOptions settings={settings} />, + description: "Run LiteLLM's OpenAI compatible proxy for various LLMs.", + requiredConfig: ["LiteLLMBasePath"], + }, { name: "Generic OpenAI", value: "generic-openai", diff --git a/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx b/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx index 5c6b3798c..b6ae8cb20 100644 --- a/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx +++ b/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx @@ -17,6 +17,8 @@ import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg"; import GroqLogo from "@/media/llmprovider/groq.png"; import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png"; import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png"; +import LiteLLMLogo from "@/media/llmprovider/litellm.png"; + import CohereLogo from "@/media/llmprovider/cohere.png"; import ZillizLogo from "@/media/vectordbs/zilliz.png"; import AstraDBLogo from "@/media/vectordbs/astraDB.png"; @@ -168,6 +170,13 @@ export const LLM_SELECTION_PRIVACY = { ], logo: CohereLogo, }, + litellm: { + name: "LiteLLM", + description: [ + "Your model and chats are only accessible on the server running LiteLLM", + ], + logo: LiteLLMLogo, + }, }; export const VECTOR_DB_PRIVACY = { diff --git a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx index 966253f47..25b465229 100644 --- a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx +++ b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx @@ -17,6 +17,8 @@ import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg"; import GroqLogo from "@/media/llmprovider/groq.png"; import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png"; import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png"; +import LiteLLMLogo from "@/media/llmprovider/litellm.png"; + import CohereLogo from "@/media/llmprovider/cohere.png"; import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions"; @@ -34,14 +36,15 @@ import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions"; import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions"; import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions"; import CohereAiOptions from "@/components/LLMSelection/CohereAiOptions"; +import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions"; +import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions"; +import LiteLLMOptions from "@/components/LLMSelection/LiteLLMOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import System from "@/models/system"; import paths from "@/utils/paths"; import showToast from "@/utils/toast"; import { useNavigate } from "react-router-dom"; -import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions"; -import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions"; const TITLE = "LLM Preference"; const DESCRIPTION = @@ -164,6 +167,13 @@ const LLMS = [ options: (settings) => <CohereAiOptions settings={settings} />, description: "Run Cohere's powerful Command models.", }, + { + name: "LiteLLM", + value: "litellm", + logo: LiteLLMLogo, + options: (settings) => <LiteLLMOptions settings={settings} />, + description: "Run LiteLLM's OpenAI compatible proxy for various LLMs.", + }, { name: "Generic OpenAI", value: "generic-openai", diff --git a/server/.env.example b/server/.env.example index 5e0233b7b..4be9ab75e 100644 --- a/server/.env.example +++ b/server/.env.example @@ -79,6 +79,12 @@ JWT_SECRET="my-random-string-for-seeding" # Please generate random string at lea # GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096 # GENERIC_OPEN_AI_API_KEY=sk-123abc +# LLM_PROVIDER='litellm' +# LITE_LLM_MODEL_PREF='gpt-3.5-turbo' +# LITE_LLM_MODEL_TOKEN_LIMIT=4096 +# LITE_LLM_BASE_PATH='http://127.0.0.1:4000' +# LITE_LLM_API_KEY='sk-123abc' + # LLM_PROVIDER='cohere' # COHERE_API_KEY= # COHERE_MODEL_PREF='command-r' diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 7b4f21eeb..68d1d0dde 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -408,6 +408,12 @@ const SystemSettings = { TextGenWebUITokenLimit: process.env.TEXT_GEN_WEB_UI_MODEL_TOKEN_LIMIT, TextGenWebUIAPIKey: !!process.env.TEXT_GEN_WEB_UI_API_KEY, + // LiteLLM Keys + LiteLLMModelPref: process.env.LITE_LLM_MODEL_PREF, + LiteLLMTokenLimit: process.env.LITE_LLM_MODEL_TOKEN_LIMIT, + LiteLLMBasePath: process.env.LITE_LLM_BASE_PATH, + LiteLLMApiKey: !!process.env.LITE_LLM_API_KEY, + // Generic OpenAI Keys GenericOpenAiBasePath: process.env.GENERIC_OPEN_AI_BASE_PATH, GenericOpenAiModelPref: process.env.GENERIC_OPEN_AI_MODEL_PREF, diff --git a/server/utils/AiProviders/liteLLM/index.js b/server/utils/AiProviders/liteLLM/index.js new file mode 100644 index 000000000..5973826cc --- /dev/null +++ b/server/utils/AiProviders/liteLLM/index.js @@ -0,0 +1,178 @@ +const { NativeEmbedder } = require("../../EmbeddingEngines/native"); +const { + writeResponseChunk, + clientAbortedHandler, +} = require("../../helpers/chat/responses"); + +class LiteLLM { + constructor(embedder = null, modelPreference = null) { + const { OpenAI: OpenAIApi } = require("openai"); + if (!process.env.LITE_LLM_BASE_PATH) + throw new Error( + "LiteLLM must have a valid base path to use for the api." + ); + + this.basePath = process.env.LITE_LLM_BASE_PATH; + this.openai = new OpenAIApi({ + baseURL: this.basePath, + apiKey: process.env.LITE_LLM_API_KEY ?? null, + }); + this.model = modelPreference ?? process.env.LITE_LLM_MODEL_PREF ?? null; + this.maxTokens = process.env.LITE_LLM_MODEL_TOKEN_LIMIT ?? 1024; + if (!this.model) throw new Error("LiteLLM must have a valid model set."); + this.limits = { + history: this.promptWindowLimit() * 0.15, + system: this.promptWindowLimit() * 0.15, + user: this.promptWindowLimit() * 0.7, + }; + + if (!embedder) + console.warn( + "No embedding provider defined for LiteLLM - falling back to NativeEmbedder for embedding!" + ); + this.embedder = !embedder ? new NativeEmbedder() : embedder; + this.defaultTemp = 0.7; + this.log(`Inference API: ${this.basePath} Model: ${this.model}`); + } + + log(text, ...args) { + console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args); + } + + #appendContext(contextTexts = []) { + if (!contextTexts || !contextTexts.length) return ""; + return ( + "\nContext:\n" + + contextTexts + .map((text, i) => { + return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`; + }) + .join("") + ); + } + + streamingEnabled() { + return "streamGetChatCompletion" in this; + } + + // Ensure the user set a value for the token limit + // and if undefined - assume 4096 window. + promptWindowLimit() { + const limit = process.env.LITE_LLM_MODEL_TOKEN_LIMIT || 4096; + if (!limit || isNaN(Number(limit))) + throw new Error("No token context limit was set."); + return Number(limit); + } + + // Short circuit since we have no idea if the model is valid or not + // in pre-flight for generic endpoints + isValidChatCompletionModel(_modelName = "") { + return true; + } + + constructPrompt({ + systemPrompt = "", + contextTexts = [], + chatHistory = [], + userPrompt = "", + }) { + const prompt = { + role: "system", + content: `${systemPrompt}${this.#appendContext(contextTexts)}`, + }; + return [prompt, ...chatHistory, { role: "user", content: userPrompt }]; + } + + async isSafe(_input = "") { + // Not implemented so must be stubbed + return { safe: true, reasons: [] }; + } + + async getChatCompletion(messages = null, { temperature = 0.7 }) { + const result = await this.openai.chat.completions + .create({ + model: this.model, + messages, + temperature, + max_tokens: parseInt(this.maxTokens), // LiteLLM requires int + }) + .catch((e) => { + throw new Error(e.response.data.error.message); + }); + + if (!result.hasOwnProperty("choices") || result.choices.length === 0) + return null; + return result.choices[0].message.content; + } + + async streamGetChatCompletion(messages = null, { temperature = 0.7 }) { + const streamRequest = await this.openai.chat.completions.create({ + model: this.model, + stream: true, + messages, + temperature, + max_tokens: parseInt(this.maxTokens), // LiteLLM requires int + }); + return streamRequest; + } + + handleStream(response, stream, responseProps) { + const { uuid = uuidv4(), sources = [] } = responseProps; + + return new Promise(async (resolve) => { + let fullText = ""; + + const handleAbort = () => clientAbortedHandler(resolve, fullText); + response.on("close", handleAbort); + + for await (const chunk of stream) { + const message = chunk?.choices?.[0]; + const token = message?.delta?.content; + + if (token) { + fullText += token; + writeResponseChunk(response, { + uuid, + sources: [], + type: "textResponseChunk", + textResponse: token, + close: false, + error: false, + }); + } + + // LiteLLM does not give a finish reason in stream until the final chunk + if (message.finish_reason || message.finish_reason === "stop") { + writeResponseChunk(response, { + uuid, + sources, + type: "textResponseChunk", + textResponse: "", + close: true, + error: false, + }); + response.removeListener("close", handleAbort); + resolve(fullText); + } + } + }); + } + + // Simple wrapper for dynamic embedder & normalize interface for all LLM implementations + async embedTextInput(textInput) { + return await this.embedder.embedTextInput(textInput); + } + async embedChunks(textChunks = []) { + return await this.embedder.embedChunks(textChunks); + } + + async compressMessages(promptArgs = {}, rawHistory = []) { + const { messageArrayCompressor } = require("../../helpers/chat"); + const messageArray = this.constructPrompt(promptArgs); + return await messageArrayCompressor(this, messageArray, rawHistory); + } +} + +module.exports = { + LiteLLM, +}; diff --git a/server/utils/helpers/customModels.js b/server/utils/helpers/customModels.js index caf5a77c7..31a3eb2c0 100644 --- a/server/utils/helpers/customModels.js +++ b/server/utils/helpers/customModels.js @@ -16,6 +16,7 @@ const SUPPORT_CUSTOM_MODELS = [ "openrouter", "lmstudio", "koboldcpp", + "litellm", "elevenlabs-tts", ]; @@ -44,6 +45,8 @@ async function getCustomModels(provider = "", apiKey = null, basePath = null) { return await getLMStudioModels(basePath); case "koboldcpp": return await getKoboldCPPModels(basePath); + case "litellm": + return await liteLLMModels(basePath, apiKey); case "elevenlabs-tts": return await getElevenLabsModels(apiKey); default: @@ -164,6 +167,25 @@ async function localAIModels(basePath = null, apiKey = null) { return { models, error: null }; } +async function liteLLMModels(basePath = null, apiKey = null) { + const { OpenAI: OpenAIApi } = require("openai"); + const openai = new OpenAIApi({ + baseURL: basePath || process.env.LITE_LLM_BASE_PATH, + apiKey: apiKey || process.env.LITE_LLM_API_KEY || null, + }); + const models = await openai.models + .list() + .then((results) => results.data) + .catch((e) => { + console.error(`LiteLLM:listModels`, e.message); + return []; + }); + + // Api Key was successful so lets save it for future uses + if (models.length > 0 && !!apiKey) process.env.LITE_LLM_API_KEY = apiKey; + return { models, error: null }; +} + async function getLMStudioModels(basePath = null) { try { const { OpenAI: OpenAIApi } = require("openai"); diff --git a/server/utils/helpers/index.js b/server/utils/helpers/index.js index 72fbfc6e3..dde8d7ab4 100644 --- a/server/utils/helpers/index.js +++ b/server/utils/helpers/index.js @@ -86,6 +86,9 @@ function getLLMProvider({ provider = null, model = null } = {}) { case "cohere": const { CohereLLM } = require("../AiProviders/cohere"); return new CohereLLM(embedder, model); + case "litellm": + const { LiteLLM } = require("../AiProviders/liteLLM"); + return new LiteLLM(embedder, model); case "generic-openai": const { GenericOpenAiLLM } = require("../AiProviders/genericOpenAi"); return new GenericOpenAiLLM(embedder, model); diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index e2b1d2e1c..8630d85a1 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -160,6 +160,24 @@ const KEY_MAPPING = { checks: [], }, + // LiteLLM Settings + LiteLLMModelPref: { + envKey: "LITE_LLM_MODEL_PREF", + checks: [isNotEmpty], + }, + LiteLLMTokenLimit: { + envKey: "LITE_LLM_MODEL_TOKEN_LIMIT", + checks: [nonZero], + }, + LiteLLMBasePath: { + envKey: "LITE_LLM_BASE_PATH", + checks: [isValidURL], + }, + LiteLLMApiKey: { + envKey: "LITE_LLM_API_KEY", + checks: [], + }, + // Generic OpenAI InferenceSettings GenericOpenAiBasePath: { envKey: "GENERIC_OPEN_AI_BASE_PATH", @@ -469,6 +487,7 @@ function supportedLLM(input = "") { "koboldcpp", "textgenwebui", "cohere", + "litellm", "generic-openai", ].includes(input); return validSelection ? null : `${input} is not a valid LLM provider.`;