From 5b80852b46400cde76dd17793332996ea92ea8c5 Mon Sep 17 00:00:00 2001 From: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> Date: Wed, 10 Jul 2024 00:54:36 +0800 Subject: [PATCH 01/89] move index and helm pacakge location --- deploy/charts/litellm-helm/index.yaml | 88 -------------------------- index.yaml | 30 ++------- litellm-helm-0.2.1.tgz | Bin 0 -> 168152 bytes 3 files changed, 5 insertions(+), 113 deletions(-) delete mode 100644 deploy/charts/litellm-helm/index.yaml create mode 100644 litellm-helm-0.2.1.tgz diff --git a/deploy/charts/litellm-helm/index.yaml b/deploy/charts/litellm-helm/index.yaml deleted file mode 100644 index 5c6b7545492..00000000000 --- a/deploy/charts/litellm-helm/index.yaml +++ /dev/null @@ -1,88 +0,0 @@ -apiVersion: v1 -entries: - postgresql: - - annotations: - category: Database - images: | - - name: os-shell - image: docker.io/bitnami/os-shell:12-debian-12-r16 - - name: postgres-exporter - image: docker.io/bitnami/postgres-exporter:0.15.0-debian-12-r14 - - name: postgresql - image: docker.io/bitnami/postgresql:16.2.0-debian-12-r6 - licenses: Apache-2.0 - apiVersion: v2 - appVersion: 16.2.0 - created: "2024-07-08T11:05:19.312515+08:00" - dependencies: - - name: common - repository: oci://registry-1.docker.io/bitnamicharts - tags: - - bitnami-common - version: 2.x.x - description: PostgreSQL (Postgres) is an open source object-relational database - known for reliability and data integrity. ACID-compliant, it supports foreign - keys, joins, views, triggers and stored procedures. - digest: 3c8125526b06833df32e2f626db34aeaedb29d38f03d15349db6604027d4a167 - home: https://bitnami.com - icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png - keywords: - - postgresql - - postgres - - database - - sql - - replication - - cluster - maintainers: - - name: VMware, Inc. - url: https://github.com/bitnami/charts - name: postgresql - sources: - - https://github.com/bitnami/charts/tree/main/bitnami/postgresql - urls: - - https://berriai.github.io/litellm/charts/postgresql-14.3.1.tgz - version: 14.3.1 - redis: - - annotations: - category: Database - images: | - - name: kubectl - image: docker.io/bitnami/kubectl:1.29.2-debian-12-r3 - - name: os-shell - image: docker.io/bitnami/os-shell:12-debian-12-r16 - - name: redis - image: docker.io/bitnami/redis:7.2.4-debian-12-r9 - - name: redis-exporter - image: docker.io/bitnami/redis-exporter:1.58.0-debian-12-r4 - - name: redis-sentinel - image: docker.io/bitnami/redis-sentinel:7.2.4-debian-12-r7 - licenses: Apache-2.0 - apiVersion: v2 - appVersion: 7.2.4 - created: "2024-07-08T11:05:19.317065+08:00" - dependencies: - - name: common - repository: oci://registry-1.docker.io/bitnamicharts - tags: - - bitnami-common - version: 2.x.x - description: Redis(R) is an open source, advanced key-value store. It is often - referred to as a data structure server since keys can contain strings, hashes, - lists, sets and sorted sets. - digest: b2fa1835f673a18002ca864c54fadac3c33789b26f6c5e58e2851b0b14a8f984 - home: https://bitnami.com - icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png - keywords: - - redis - - keyvalue - - database - maintainers: - - name: VMware, Inc. - url: https://github.com/bitnami/charts - name: redis - sources: - - https://github.com/bitnami/charts/tree/main/bitnami/redis - urls: - - https://berriai.github.io/litellm/charts/redis-18.19.1.tgz - version: 18.19.1 -generated: "2024-07-08T11:05:19.308028+08:00" diff --git a/index.yaml b/index.yaml index 8faeeeae6b6..5c6b7545492 100644 --- a/index.yaml +++ b/index.yaml @@ -1,25 +1,5 @@ apiVersion: v1 entries: - litellm-helm: - - apiVersion: v2 - appVersion: v1.35.38 - created: "2024-05-06T10:22:24.384392-07:00" - dependencies: - - condition: db.deployStandalone - name: postgresql - repository: oci://registry-1.docker.io/bitnamicharts - version: '>=13.3.0' - - condition: redis.enabled - name: redis - repository: oci://registry-1.docker.io/bitnamicharts - version: '>=18.0.0' - description: Call all LLM APIs using the OpenAI format - digest: 60f0cfe9e7c1087437cb35f6fb7c43c3ab2be557b6d3aec8295381eb0dfa760f - name: litellm-helm - type: application - urls: - - litellm-helm-0.2.0.tgz - version: 0.2.0 postgresql: - annotations: category: Database @@ -33,7 +13,7 @@ entries: licenses: Apache-2.0 apiVersion: v2 appVersion: 16.2.0 - created: "2024-05-06T10:22:24.387717-07:00" + created: "2024-07-08T11:05:19.312515+08:00" dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts @@ -60,7 +40,7 @@ entries: sources: - https://github.com/bitnami/charts/tree/main/bitnami/postgresql urls: - - charts/postgresql-14.3.1.tgz + - https://berriai.github.io/litellm/charts/postgresql-14.3.1.tgz version: 14.3.1 redis: - annotations: @@ -79,7 +59,7 @@ entries: licenses: Apache-2.0 apiVersion: v2 appVersion: 7.2.4 - created: "2024-05-06T10:22:24.391903-07:00" + created: "2024-07-08T11:05:19.317065+08:00" dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts @@ -103,6 +83,6 @@ entries: sources: - https://github.com/bitnami/charts/tree/main/bitnami/redis urls: - - charts/redis-18.19.1.tgz + - https://berriai.github.io/litellm/charts/redis-18.19.1.tgz version: 18.19.1 -generated: "2024-05-06T10:22:24.375026-07:00" +generated: "2024-07-08T11:05:19.308028+08:00" diff --git a/litellm-helm-0.2.1.tgz b/litellm-helm-0.2.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a02cc50daac56d3eb86653e156b0197bf8f5040a GIT binary patch literal 168152 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℘JW-gz69rH$eug6;(njzKWJ<$8 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%0A<@>zV-Ww?zuunO!6U@{OI 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 |7!G3II4xk}OF31JfW0p38VBvNMSdqpQ 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 literal 0 HcmV?d00001 From e6e83b57a2d1f4399aa703f2e414054c634ef909 Mon Sep 17 00:00:00 2001 From: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> Date: Wed, 10 Jul 2024 01:00:10 +0800 Subject: [PATCH 02/89] update index.yaml --- index.yaml | 45 ++++++++++++++++++++++++++++++++++++++--- litellm-helm-0.2.1.tgz | Bin 168152 -> 168956 bytes 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/index.yaml b/index.yaml index 5c6b7545492..8c53e745fed 100644 --- a/index.yaml +++ b/index.yaml @@ -1,5 +1,44 @@ apiVersion: v1 entries: + litellm-helm: + - apiVersion: v2 + appVersion: v1.41.8 + created: "2024-07-10T00:59:11.1889+08:00" + dependencies: + - condition: db.deployStandalone + name: postgresql + repository: oci://registry-1.docker.io/bitnamicharts + version: '>=13.3.0' + - condition: redis.enabled + name: redis + repository: oci://registry-1.docker.io/bitnamicharts + version: '>=18.0.0' + description: Call all LLM APIs using the OpenAI format + digest: eeff5e4e6cebb4c977cb7359c1ec6c773c66982f6aa39dbed94a674890144a43 + name: litellm-helm + type: application + urls: + - https://berriai.github.io/litellm/litellm-helm-0.2.1.tgz + version: 0.2.1 + - apiVersion: v2 + appVersion: v1.35.38 + created: "2024-05-06T10:22:24.384392-07:00" + dependencies: + - condition: db.deployStandalone + name: postgresql + repository: oci://registry-1.docker.io/bitnamicharts + version: '>=13.3.0' + - condition: redis.enabled + name: redis + repository: oci://registry-1.docker.io/bitnamicharts + version: '>=18.0.0' + description: Call all LLM APIs using the OpenAI format + digest: 60f0cfe9e7c1087437cb35f6fb7c43c3ab2be557b6d3aec8295381eb0dfa760f + name: litellm-helm + type: application + urls: + - litellm-helm-0.2.0.tgz + version: 0.2.0 postgresql: - annotations: category: Database @@ -13,7 +52,7 @@ entries: licenses: Apache-2.0 apiVersion: v2 appVersion: 16.2.0 - created: "2024-07-08T11:05:19.312515+08:00" + created: "2024-07-10T00:59:11.191731+08:00" dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts @@ -59,7 +98,7 @@ entries: licenses: Apache-2.0 apiVersion: v2 appVersion: 7.2.4 - created: "2024-07-08T11:05:19.317065+08:00" + created: "2024-07-10T00:59:11.195667+08:00" dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts @@ -85,4 +124,4 @@ entries: urls: - https://berriai.github.io/litellm/charts/redis-18.19.1.tgz version: 18.19.1 -generated: "2024-07-08T11:05:19.308028+08:00" +generated: "2024-07-10T00:59:11.179952+08:00" diff --git a/litellm-helm-0.2.1.tgz b/litellm-helm-0.2.1.tgz index a02cc50daac56d3eb86653e156b0197bf8f5040a..acecd9f943acc54a919b1f4f995a969ad2b64982 100644 GIT binary patch literal 168956 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�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 literal 168152 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℘JW-gz69rH$eug6;(njzKWJ<$8 z+E~VZY+@}6$cwO8VFooRhBcl=P`qgAWopv-g+hc@qL(C5`o0}=o&vw10!(L?tXT!U zHGLfGn&$p)=P6kdQef%{hQFMmPfqLGl4{%9W@nmj_H_`x4RQ*2mSH7j;rTUQ zH|7K_Uxh2wasQKy<63%Y4)82Oy6T3!DUn~CSg+=eAh$`W7Nt7(R>u8Wn#*?LS|COf?hGEGMxFa&+rQnWK@d_{6D~SFxdxu`QAz z$G3IO_ZL(M7Ew(d#Uu&g#WVbqjgKdT0s9EuUQ_C_eL=NEY2I{kntIOe<&RjAr9aK5 zZn){Z1z4f3pj|>+!QMBrTU?0w+eu(oAZQwskV5xwUFe>6ufHgg0d6pf<0*g%Um*gMu&dJH{tnFV~1d`$gUnOtjV)K)PYGy4dkMohwr1=Xu}$GR@QgrqcF z4LSz{=L=#w$wFFquxI%L2f`hLR_Mvw+0KT!u2@kdIy`Y7GhA_X8Y{GdyRSlgoLC#+ z*7)kpGXsa|rO0!$xfXt1c!FgtEc9l^T?E-?i~Mtu(7aO7B%CfKE;g%D4L0V>^AOWr zLG&>8DKf8T{c@}XivRdvZ?X=1%9UFi)y%QLjDAo3Q+ed0Ij`^kURfni3Q_U@mniiKq!;HJ-$Z_3}#IwGvnYAs_eA`^*S` zZJ09lQ^T44b(^t#f``A{oJ*5NMutI=tjD464){doAZp7#>egA>?uHv)ex`z&s0S4w z2CEB!;)e3yb>;s&Wbgbtz&!u|7cVRMUpBtxf4`gZ8S}q86vCHLD%Co2YT*6fX{ex2 ztX7b~kX_IOQb92={mF<*YX4<8yryY=B3THE8Z#R$ttVW;m|7K>W7kag!=p*ql;tYN zYcoPQ%~e7YJ}0?ywTsN0@?>P?5i@Z^vvKj7@`i#Ed#`xD@eQodk0DJJ&~!w{RFJqQ z6&GYkSF@2HJ21E>h7Q;%+G$(k-L4xYmo?H!7rL7p8{;l?$8^laRR4SH+qbL>-O7+% z=sw?g%et$sL$iu=}n~bkHaogLi&3yCuv*sPFOg$#v zyUsim4>k&zvax+L6c3&kFlFQA0x14*k%?~=C%(P$-OX^k*xifG?FDdrYoVEM7l-i8H7^w(*VX%+88+plffX`Yvkj zQ<|zwvp;7Uc$d2B!Rx{g>sjof_o<1&Yiy+TY}|9NX9Dj0tnqjIZiyD!M$onTDSVIx zOdRWpuUU9r6E@S_vw1=-oiP@L%*njmSO>e8StGt2Ot}e#?c8SC|w^DmgFCB6Cb(Nn{g4QmF@6jIOT9WKtpwFA`nkCDZivJ zA{TlQ7n=H)Au9FF-%tAWsjb1IdXxrfblG5@S(0sOcCL6u%?;c`uJ;gq2#PN_g}-MykJ+PN!C2@OQ1*%0$tStu>~ch{7Ab=zR`$!cP)*;ZIPkW;>Q#E@6J@hZisimH?`C?Z(w7408AW6i>8Q@GeW;>w=-*X;Q zk`%l8WkfMzpW;BG2;_9!=dpB4p3`;ydQ~Ill=j0s;vr$i*ol%b`x>M7*N8Um?NLtp z)^=iEd`jq50#xcI`j+`Vc}Fjsmi@{y1HDc$p#dBWP|R1{p#2)ibC#MYnbJMEB=i#S zMyq&*Ls?j#v-JBH+cXJV42%Q$BkP&0LBf5egAcPdHf*0r`eGYs5{hZ6+;Pxj{$I3H zy~&Vh=_<2#U=m>nNx>)9uEm`peJHqyFvg?J*nuz5gGqw=XB&PLcdH)#i+A|aePe{`xv>X*?&;?q}OD$^hx5!*oHR%zYT62J_owqos~p9OG)bSIvA zq^JZgyW%}Oo|a5iA!@hJ(o~t!&^)W!B|I@7o)iePnNU8kURyrBxw2K2r)N^f<7{R- zyU^`I7BkNhzS7S*o%L)7x?SIVAbUKq9a|RR)!*EJz%D<3X)!q9FzX*i{woV96UAZ~ z45|7Do&F&BShLJbK+(9>g`mV&J?Wt0a!?r-+sqsoYv`f>;oV(O2C7}d%+byEydsL= zMKht-ax**?3A|NXOvz2cpMpeILWtC`wIFm zwj&yP!C|bovx!gh2Lw42M6$nH7c;OX0`f`De17YGBY*2c#HmT}WQk%XUzzW}C0z#u zH@9;1TRXLeoo`rqLv1glOMQ6V7K2@DsI2p1puJMuESt!n7#Y$X>s|jWHw77b?7V6Z zJt9#Kjb4xU?zZ5z!$n2MZRxscbiEnX92IZlDpSGu#@7n&UR_nMx)mR4B7>WV>-7`y zE~2*;;k|h-wN#}T94fHFc4_mEmB?1I67x$cU`2DPy*M2a*j!yT|9m$YQ^7*mT!ocM znpSIF*)Zv+-KH~x^<~Idg%dP2phNoc$k^fT#qk@aQ2p5b?YCb%SYSNUD=n8xCE$76 z&i%Nd-3$_W4exwifgDW+gP4{G+N^bIxV)Q;>u1w&>GzZtfhVn(*(cJ3e4712t#VXY zPXezPQTafeV_w%S4EaZ)!&&b66G^2fW?@{bVtf`&3)W1Yq)R5z178ADHVcK3=bV^e zFQ6@S7KU>!s;RK!e6UVTmTHcthT<-%R>5CXLoc6Oo3P+5daiWqc0W@dUN|YP>;3p? zg{_hko z1X4jS7@x@M-HG^@aM|Xto0{_u`)5173u5;?6S(d%3p#hq1W1 zjrf0?TQ9dN_8-rleYO9%i}JbhKlt%ij}0yX#(b(JtQOfqT_F!Vl%?khY5i-gS0N89 zKieawG*qsIvLA4c`0(d-9uz+M;;xM#U4GHC0i!F;3Q>LwUw3CWn)yhMM6=q;ep;b_ zpdmn^N;&ec0vUDvv~`l-7L zD>l%Jfn%Xn*N(PY49!%p0n?MP1Ng}vs>-gi=Ip|$zMD;9)}MCX;w*{LQ<@Z$S&q^w zHoKgJde2-OpuqN#3L4S~WfTm5oVb*O2)L0_>CXE4I1jWQSSu;U*JUc$a9u-61|v0& zA0g~|`q<%Ltxkpuk_ELU&v-NE*>9PG4+w|OzE#oCL4D2ys;jF&v z=Zxc>@Yl)32O?ZRL;5l0LV>0ru7kh++I_roKIzj?#Rwdy5%k>lXU6ah~}`CDgWFAb);Dwo9ZG0bvxvx9SYZl<|O zW<6{Ewa^i(bewCK^=ho~jkkkAxA+cG|HOjKOTF*^u0K33nIn|MjC`bu3Afizuf|X$ zHBmSOy0%VXjLVY`-aT-Y(0bk?nO1xs;`;=Ae#?igZf=Y zZb`<-PyFHN4U&J#Qnr?vMXQ~>T$q>Ju(h{shB{QT2mFReA(z4!c>pdAIh>NZX$VVFSTju(%w<&TrLT6Df2oKE*QieBC0_&<2(XiaH{DIgM@4tM-yX?!@ zy*&M_`m^l*?+#~t==RzIcI^LNy?kB6|9rLol>hx8KM%hD`$Z@Y!q>FOQNibZl+ieb zsT)t)CTXp6viv2UwA`0?PHEmYCp)Q`XJ&v^U3x-Q;^7b(SGaG^d5X>o0#fI6pl({b7X8%hsciCh;6)gg28UGRp(s(kQG;8_Z8^KqB#NT$Is7Bu+zmrq0d` zX~29b1<29Ni=1W=j@b_6mjJN1C#id!x+kSaGS==*wUvTPl#x7FtGTubBs3J~IgU-( zx_(-taG6X`fxNcHFdUV|$W2AHx{B5P_#>XBG1O8IqFtn#fJJ8_Bso*b0GYgg4t}|% zaWNxjB%4Kyi7$r^!}U^|(TU6i2L2Fcte7chG^CNxIHq^T<W^3KMG$8p$NIc_{&n8U>n&BuhR8{pAEUa5%UNj;DU^0>W zIw^u-DIPmDxZcFlmr#2(tO&Ub6gH*!rVOTPNDa1cBdrSsXGGvjaz(63h@s6Hg_1Zy z3rgp!8@Zc;eYx22+pPRT5QJpIl$gWTWusVGu3C+f~fQFMwK zlF1iu#IS+f>@1U~kPu-MU=fd=Q^;smSjXaC zMYPLj8s0a#NSlXD;6#+1>*#b#UMkpU;?x_1FL6iX7TT?g1@40`i=uDQ?ibT3HCd}n z-vBeR4f)&x+yW*s$z?fh)ns`OjqK4x*KFXvgxwW5ZPA7sY?KLl3G+AHsc7)0TW`+F z$~yDW2e2l~56&dorPNK+0v*%LFfx7gz4f<+&C9fH3u)Ib>jeh^%|bam=CsJr9nEfH zibEJaY-IY^YMmjkkXM(-8+NH}?%G$Mz-5X7O#+g_emEINlOh9-<>gAA7KCB;?mYIO z_uzkB;t{I}QpFP_eSALQr3_y00gkZ{9MXia%Ds?I!w zsfbS^kb&5mVdY4sEcHinC^B0Xuyab&|aat*hhZ$Ss?)Sl_GJ z&E4t!JHy^^xAuY=8_?V%s$ng%w}K2Hquh-J;Rkw5jxtww+HW53*5>J_$Im+Ue>0p#IJ|B=3hvndy?*_?djI>Y*LzRBRD?)(6=VzphLs^fn4d=8N=wBu9XOLDl#r%P}8Dzw~N zKAZ*9C?`Q)WX>qMDnmx`Dy1TyHg6p(bofHcKWNxCo>Q7PY#lS>)zGTdL5DBDD$LxV zQOL)g-*<0c+rK{-hX7nfnwnEDQK$Myx5U+6YTOoE1)I0VQb#=A#cSRjTO8P}0VYjF zSq3k#rf{wuvg2S9=Isg(jq1_u8=`vfVm2St(VBgR##nG1h?afn9#qZR z=P2Jp3CpkUK};^5+9mcs#gWrz1zejiXN|@{AHJ7x<|wibddoO+dXibQ8%H%_fqq}o zh0{c3MF)-|`^eol=5)Hq7_*GJdvxC1<8ZT@u*x_ySENM-R>OB|cFghdyFkyDX7(4^ zt0ns#J!;O9t{yX=FYAU|i~olv6S}njXK(lAi)#G;tJlwWpZq@$@$(t|KU`~$zWeu# zO#XBIP9Uk0kH-mQwzk<5B-mcj7i3m-PY$8Fco%aB$?w2awm1|6*z4Q?oSeB7*N zi}*`SE;{z2_u%~!J_wVAN9h)mQ{Ddnx0v|A9AvB1s_Aq);{fQ{ZgPc}`}fIJRv}EQ#vXhx;}ei% zzii)|Y{UZnzO3)9fy%1BH%a!9``&tcXqqhO`}EMPd7snRhAuaaF`$-k)7+~|v~5fB z<#x^Leo@tB_wN_|+zy_OpSAe^;Leq1Ads&3uU9W>@n0`q?LGPb9^&US`v2w^|L5)c z9e>vSKcC<22aUYf$SnYl+zQczR z{Xf8|^|@octeNvsd6hnMq?b^@r#P?A<7?_%^Va8STGztg&GRxlEFk)2`mi<|W z|EDbPb^>+8f9<`l`F~!&e9HfOn4iz*|2Y#{b01D1Lm?lHC#dA9*&S4tS(NTx7S89naa@Xc`toZg~^BKJ< z^gao^ZzaiYPA&mVCCYd39wg81dB&$JQl0H)QV7v8Ud( zt!r?Vhig%TtAse`*Fi|=?<86@01nn?Pj%44PM+n zq%FHSE&J)lIu<3oW>=Nb#052GIj2<%p%pz>W@kNa_my&X)J(z!yP(0WyhmrqdbbC? zTXZk8M_KfPJ`nAmLBLk756u?6MdlbwYbIm4D%fDtZ6%w5^=qq;yT*H%NlasPxv9i9 zcfVjQcD84IvzsaC1!uY{)ecf?p6|@s(&eW-=ic@UR+q*$b*M~(N~D!xZt6o@xkdfQ z>+t_(B#hcF0_(c}>&5P?TKwPM)BRr$^0OZQ?@|unbFMb`lm06-^3nKyft_ZzFT7sO z<11>oA7^h(nEl+c;inwy0r{u1Y05R&Gse z`M8j%vf^A!?-HXCv&L=mG z6?bNW=%aKwi4{*=9Hc4IUY}aNgq{Yi(9Y^RnOE&Egxg&)P*VZYS5LFyab8 z&OcCm#*{>vu;KA#-W;*U9crH?#OhK1tYiOgu2n8CK*#?7#p~zQ{r~G% z&!6)D9^~iq#ed1|FfRie{cBR+>kHc*%EQ1uq{(=9mrpL6Rg-V1E&zu|!=}By4rY)h zlDE4n&E$sK_SRe5p02Hr$=77*iF>4-vT-$`esy%E8&GO2_NtX`C&%TK2vbDfm8rN^ z*E6ciB4rEacotjPj^$>r!Jkr^6^;HBcfAZvNCMaYhCBV!ETN0K>lejj+axm~&au0S z=D3F*c73^2$t$Y0cg)%_b!!icl1Xj5Q0lO+JGyaSWl8x+*dl1c?(n6fsFk{2BkfA zsy@pl&0Ax{H6>RHlUg;*@KFti)-}Sza`UsLk}!=Y_0#U#+Bx@Ew^(P4^5GztGONgh z@VzDeOl9(7~?QR&peNtMSQ#U z<||%Y&H|hZoP<8#g_WvN_P+5<39u%KRrfUu+5bK-6-^SFkwt9G4*P%i<;&fQ{lCBe z;>rGhke>~7hI2m0Ft}?T`j#&k3A!$#7%tgPad3ktg!zLFbUBR}Vnv$LEN6&KNgPA8 z3Yy_Om`2HD8)YQMd2}lmyz)6t!odbg$V9|9ZwjY^j3Y(=Ys*LPlX#A30$*~LP)afs zM+xx<{?Wy67kqgdY@kC>M)dRH1q!2#4g5)z@4$Zr`vd>_U)c`)S3Q_ccKCnPpX@f- zDG6QU;HF4n*TM#0`|K_qeC=Q3o59z9K1&B*|Nnyx^fS&PS}=5Sbj${Rn$drdARqWq zNbrs*m(hO={96{#kZ{`3Y=Xu#4oNnMW|N&q>tR@E{GT5m9KAdCXW?RXbmV`%+TDL$ z8UK6FU%z-7{}1uAfxe6K1kWNh8s0|N8MikMdCw092k6%yNj$S3|9w+klF!w~XH#)K zeooUUU^}rW`kU094PIsoJlB(q7Ac=ngg&8|PND!^ll+d5L^C43^9O^C4RrbTKab7_ zgR846ZW9kel9D7ONe~ffck@+oBfQ&Ejw(XYbYgD!FMuL2#ErO_eIaZD#=M@!-O z0iErTBqv##MT~51gCSEsKqQQS03GZ8bkG%@dy?dup}_!_7Ge0L8NH1{!Vp%PqCAgi zO5&7c4BbtmV2T2qaFYv;8z8abmc%qg3AvM6gbHrR3mvX$?Uq{F&`MlhZZ1^p;NL0qt$WK!YJ zG6LIu7UhHu1|H(|WeLd%Lwo-I^Zyulg1~eCum0|Th;R5llyDmG=S50_2*)T&(jrGJ zn35TWHyh|=h9~5l(!A7JH59md<@1z`5QA;k0HKhuAd6t{H9|))PLL{YY$_v8l<%`* z8t}c>03q@b&(fHTkRDxwfiVg@syf#tL3)Bd9}OmDsgOI#BuYLG205N|kri}uJTb}a z`FpSYJuf8J5l+0lT`zm}d@x9hI6k9s6m;?m!XD2N7Ua+Lhx)7w46A1c@g1JCXMDS) z`QS2p2LAN?*|s>f;`;<%{A7Ge^RtXFlH|_>KjOTfl?>r5!}9?`QBH&xqMcK%tKCwKx*2iAJ;qj$fiNn$rT^(IO5}9NiH9 zh7p3u$2`L*O2#ysi77&?3(V{!aahQ8`yfG4!g8DhL=w9uqrrw&OH<=Etcl;&mkloe zhTh*mM)xDLCk7ixG+lB8^DZA(XMEOYIY|Oijx2c)+$kq@dMe6uqgT=LO(AeO4hAfz z8J>{C7&F$Tn?Fn`6AqV>!5sZe<6=h8g&>50(l$C7E8QD-V(6f{#3 z@R{d^%wgt-g@C_FjGFn&C~0Cs@K3K{r`9HHVTx&1H;~Ef2144G$P1~^nxDvAi;~L{ z8q66h_)PT+-$38VI8Ve#!Xnrb!&n6D8`DIH5`-n=+lbK_L3uPIj1#!S2@J{!|5{jf znFvToDPz%f6i4}7R13D>ltdY;N`spgs&_ZanS4je3Z8qO=QdxZRF=105sL9OiRB7~ z)3pDmq6^=r_@4#QHgMS+zyFSOVuf<|{(-Jvy&y@TQ#L<+8d!}E)3|K0@B7ltNRr!; zPF_~qinD`@i(lTKAL$oJ_!hs<=xk&>L{iWYa5lEzw1`Xfj%cxT6xUD?+!=<|vkAM( z8!~UL@y75>_pMOoTa|&0pFV;71{>zF1&FmWqU^!nHJZ@R<^iT0V5jddk1zcEV{Yxq zRyZKu-F?_2;@!O^4vG6>-gX5Bu6$e!eRDGAGt!u50be;O(?|F+F&xey_?#8RG1zu6 zmEoB1?R0G`4O4L(BX0eo*<7CafTO(9QE!IB?TRel42Pic&niWGdg7}Ue5D5ZZ=6KM zJKuc5Fwmopd`xMUBZJJFuQvI${oeGdLqJ*@dir+Mjz}4Q=Ci)6Al~U z%i!-zPk|g`utCTvpA=}8(hTRqhz+Chm_WCT3C@d*lL|uuXRo5E&xHjARQNcQCNKCj zbXBqiD9Ra$$9z+TvryUVUyZOT~#RPNDZVCo6u^iA3wKIqbV3rsd;Sj3g&oXSfGN?wLc=#Eb?lHU zYe+Kb#e}1&LXCDYP$I2KT)R%Bt3g`MqAaPFMB|Lk5avx2x=0hE)+FWYlOtyv=&>o^ zG9=G(H0DzmPUa|LtRSp^mBL1s<47th3rK>qi1G=7&)Kr4q|?RxI;4d0>eZB`#v@MV zydz6C%2vQWp)%Oarinqr*=B8g+rp4*Nk+ybQxny%bUcp0=?KL_8WenI1AW#$cy9N9 zyM6xKESiW8-r3NLrRwH& z`lb^*FLIhiIA%K;VKlxaa0Ipw|9$SIl;vK@is39SlAsF=4D+Zm;L+XoS!CF}_25Dw0dbEv5B%FdD`Q*zugV79( z1AlNqh*i0AmI=3R6rdO_;{W$v?Y(}A|9OxfoFr7v1ET>#0nW(;&P&u4p#vn2 zhS&)GF)*EDa&IR$fKbLbeNq1A9a(I&=kI^x@9U$MeJ^`%k)zCmaH1}Hs@!PLBDVL+ zq6RA5MG6XyUinI_eO}9(e8Gk)M zNUeY)xpG62hP~w(;55~L0+C8wMJ=jgsQ?Ouay*ey8uE>&DORF1D#yQJ#5T{j_@asv zL{pL=MvE*U+X#oZ+;o700THk&eC$3t$$5D?&PgIHpA1f-bIJ{AEIjWl&kBKk&PaAk zGQ=Wohuo04IOz{)0(OTu^kCb3Uo$0a8p`f<;*u%Dx1khG_MkbGLL=9S+$O?_}}L*_G;_@i`P%<|3m!T&&+R3?|a|)yMr*A5SEV+o8tYKuSPg{ z`SKfb{qptez1{Gy-|W61`>$TT*ndID^H(o|@YP>mg|Er}iUcDZ_ z4qm+6-3|7ygJ7U@Rz_&Zcf#K8b8q*rmwPWqFJFyb?E1Se_V;%8_g?;g!>1{x{ik{S z8;5!;*3mlt_g_EX-G5OX|1X~Me;?*&12rW7ZlFHtzk`hp^#3p-kSl%=z|3hGj<~U* z9?I}R5&LGBI0J{`HcBX*W*4Ma3x~HOV%7 zYgTE0uER6sX&aoifdwo>{pu7=eigL~^VyhY^(d)LwpAx(BZVnq2F{y)wW&Imp(_gl z>WY&c2zn#6_dM6peQ@r(v4M_#2sPW-sP(}}Jm$NO*<5}26Opk9ry`1UV?&WPzeK*hjU4GrxnzwXrktV~ zzJY-@(=CgolUH+%GQu}kK~6&Nb{}0F9`X&wjNTHI70Chnz(}?&e*Gb%MG7NoZ2TU} zV`wEzHMk=9*pO)Q&FhuxnZL0vu5N(DOT;b}2vhbGfC;5x3A4ZyzHOtC84QmcH zCz+e#nkX;*do_I;YN(FNHZ}%EQPAg-2%k6f$1)E$r;te-!HJ%pn4R%koVZD%LR}Se z)WBc$pH z8o`6oC5O!4`;s=ksn8dTLN|RTBOx~iwJ)k1i;;3f`EK9TGBt!k@kVuH87B!Xl7Op?3Au|-#v=Y(B)_Qdg~4(SF&;F&6{ZJH(8(DWYes}MU{WD`_x z>DgLE7|Wn1T|iD0A%R&NW^5gvEz$s57*fOzlNTG7&} zMkckh_ZKIZ?LWg(m_GI3v7=`R*m$sxc@$ zCv{@-x$MR}JlDPz?EAhiH&tp+B|tDlFKu(Phmm~SvBS#oq{cy+3{&TVze6d!bbhBc zDeeztG;+Po)e@kMdC|yS_2AOT1oR zb{CMdhQ*CuNBxM30F!_w0ZAbhTaDozaW1#Bk!Hl>0wtj*9;vb1n8yTFE|?G|_gkt< zA&7xbJ6HoR1j-XHc ziCh}eWj2FpZ69c?Ql;|A(pDB&!{jJ4S5=F|Cc*Z6w4J1s-P~}RvSCrKYyzx{ehsF~ z=RP`+J0`U^x)NZDF-^!7q8Yj}%$_S=gZcT$G>UlDMl}aTJ|#)6vVQGWTbi(3(;3P0 zD49UmtvJflC_)~DUS=%!vI3-n3z4;D2*-ePR(-5J6WC(Cw&H`uRwJg)WIL!Knjlq_ zPaIT{*Jrv7^ff()FQq;mVU*z{q%*X)3-O;g;FdUqWG2OoWKkebI2AY$BYOF-`!_71 z**PJXMnV=&s%b~UFgHg;M!<`RV=+TPRtMxiCMe3EF|jIHL5mgVP^n3Lb`@|Qq3$D# zxjqdr(oc|vX}%$I87G~QJc|goZi0a>{(Te^ zhThR4$xY!RETZlhgn|+rmI-3Kmy+8kqsdGJ@n;cNSj9?}w#&kGG4XvyXHq8wH(FsK z`VYisNUdH|NuahV67%SxHI4o%^!M+|8DdYeGFDy&z{?QT5{98V1qrWZM0Gqjlq~nt zR@Ec)hsWEXM*6|~?;8mjnYJ#C9;egFz~T0lkz9XklCEKk!(ut`3qB#R&_Io?*GppYgJ zNpLxfpRIEDRV4Wc?YUVoTX5JWDao1;Slu9M&_T_ErGb@MK{6vZvT~=%q~D}A2dmdy zG*a7W;$TnHQTt#B+$A}Q=jERHdX6%}*X3k_2o9#wC7fZ(=vDorB6{tW6K?1=uV^M7hX2fH_nS z3}I?-Rd?yyc(kj8O?gXw@V<9tut{&INlXbBB)g3Q@-|9tm_`_=XPz)ojLGx{-p^6; z58(}msWYYFGuFCh)a!FtF_O15BgY>}a5&@oX^y;7DYGI$X%RL(iMxk25l(+1wL5V{}VnoJTS7 z@;n}zCAk6ZUE|;;rW21v{~~B_cmKugdRUqUx`wvK3EV`hdBR!h>NnHT+d4s28Y(L> zwp1ck~U-xLn;X92l;8>vKkbxOTwUM{2q0fv4RaiNE*EExw zrKD9;V@N(<8#fI`Yq`*fdE$&_xxB1`%S28dkPnno5i03iYpr^1Y9on}6-I1?UOj*P zjhg@EPF(rj)AA6~T2Itw?HP)rTas}5mk%axpG5GNaJa6+3s62+Re8D^PgJwBVSH>< zY|9hol_EXk1*$ti^IexP+sH;q#I5lo665)Wbfce$XDH+kwR4?|)cmxQG`D1EsED4y zU&~X=g=b!otGfOdrZ*O+6RVQvab0JvFpW6@vDShZP-_rqp9mQ9a)}b<}>lppsTdFQ%pT$34h*>gmE1duZyyG<)jm z!gPCTtAj3_ub5BoO<(ucp|4{9bYZUPhhj@*t@99eSjMD@qfxO(qls>Kofw2eG*SO25aB zK+*I9I!g;U|B*QLidgS@+T*vgQJ3>P)80RjgY)Z8cYKkII zR5cDo`6XaS9-e(bG(%#SjW!8m!t-5`3A&@%O-ykp9VZH=ZKTvtCpF3at72qSZfJwF z7@__0Z&Bw6z1-V>7pcdB=jvC_>tFHhz5L7+T{)b}UDp{6xtxS!49Sqdb!?)Pv(1wj zPdgioJQk{X!zCIcdCJI2a6KeSiP)zx4%|*I2k|0JVaSC@C3O zs5Tv*-Jox6%_?KN7q7BS$Ght+m3^T~Pib;aX})x|^81A<2K(YHi*BQsOvo_{a4bCa zCk#co#QPNRUqrmLB0EwVZn7^LWWedU45lP3Vv@nB!9fgh(m6r;dBfoVH;xur!woLEi+w9j+A=y?GBhl3Ey>V=hu()4$W{<`n@pM7?cnaG99sm!#TPpW4;T# ziJ$@zjY&S{+bAp`>Z>TL93WJwM$90NliP^Gw51T?8;$!~$?~YS@}f!eaFNp)Es|Uw zDjx(8gmFo4NFw8lBS`eo zS#{R#B_!wi8zqw+9EKiA@+>0E%PAPEBo91Z0rT#nd)fb4yT@dHZlPr~Zyyg1fYEjFO z;c^;_R1xW9JEI}O@{#nQ`RJtddR?iZF3Vg=x0fp>nT~T2jXcF!c(q`s%cYKYH@S_# zEwqCfizW%kB%vYM*?{dl{H^%$a1wer$s_&5)CsGtO|uv%Pae4hW77TeNvGE65uhjv z)=p_iP^EcN1oy!^|8I>+{;}>g4npo%w*-VYsxKkp7v?}E`NQv0RO(NJJVf@1hyS%#f3$BlLCLrw zEKhZ!YDmj+gM@bJWVvO}F=|TMF|1^a`Bl?od$n!7s|~*^xO-EVsdw9zj`39I5_e`u zzP6|2M@{P>CwxI-667>9RG@7IUtXkrM!z}U6=O}z=QP$B)0f8Y_pc6oP0pd~OCB`q z23t)koYR!XbTYq4x%GcY6Tah#lAIy&1euQ-o3ngt%B>ie3jdF%Sn?Qfz%38Ofjpus z@jDvXH*>e}$BP?sr=zf8UXDw1(WgmB)v>Y}fBIrrEhy}8Pag4k8KEgj3_;2I8m5Oh8k1li#NI|f>)G*E&4JX$q)NU*TJ0lzKKH?g(QG5=`b|wme0-Im&_Sz zm5UJ{tti*Db54s)rmZH*6Jlb#mhUUC&Wy}meaI#Yq$+Ax{#{nb-`Mql|J4ll9ihGb z=P&d|PchygGQQp)mSU+0j=x=4YRj%GyfV*74XyTp&1xBt-`)|;{m|77WxmHVHhs2@ zo_PWPFA1g;4X1EB?;YIE`|R&Z+7%YH%#GV4%`~V5r&$48RVJO~o*d?-JEN?pVZ&qI zJy}hVHJEIU>yt<2^-C2GV;s$xX_(i~JX+Ziu{G#STn&t{47P#E;tCRuj-u>JO>Mqe zlPKA|kuj@b(Z9&sXcmdHKR`~%sk)jI!^Mgs+@nv$npnTO;lljOsk}L#4<$iVo9E!o zBq74Ny9>(Up(+zwhYz*1qB69m3e8~kk-R%H#5wLEy5jmwwSX1ZVU&Tzt?zG;jp18C z@YycYgeXBFxi#}zEPf>mJ-b>IKK__yoIE7N2urmHB>b6d(fV3ra>*<1$*Bp)S07vQ zH3|T7P*OV?qmYcFgoLi!T>r~_^sVtt*CnvqeQtp@=oN|NLfjT&p4Q2#k|K_`Q9^6g zfz=>g0^2BJ@$iJvWX zar@p(KLrmjWS%x9ORw%HQvRV0E){U<8f}sD6&MDR6LqHgU(a`UcXz$#`>$WU{?=@| zZbYT|O*VpRJozZV4lgr29!EhP53M3{RUYNygEAJmmr!JU511F8ys8W?qSJ9cFrRy> z8jsJ2-ZhSK5|9iK!GIG9krc!c-w%pa$BR;X=61A9D`2NoG7RFBK?a~%uwnX`@=n=I z)SapYQ@1L@WtYRQ5`Vc$biD2?At9Gv(oMsw5b<86DW7j-{0qk>I8(H z$rzgg4BxGF>bED^k$?d^rZ}8U?AbqonnhYfA_*Zq)rgoq^=0?a;HvkX)V$W3-clSQ zRkEyaO*4d&lRXqHbJJpUv4)AQS8Ib{_a(3u%bA@oXIF0xLRV;$O26nf3SwG>`r5v= zs+62Ec^kT9(XmZ(i9=Yb=i=huzCA9JT~sYAO@BJD1w`nj!Yh<{<0vC{IF5HV6wtUQ z$&6`cNkg4I7&`@h*xb}q>-c$Z*BAf4yR-kINw(J4ozt0`i%-tlqfYi;VQ`Nhw7cU#)iejb>UY!)S;NwG+t$e0b6~(ap z_pljMQ42+bDXJRGZ*{tJh5iB%iMpcu07+E*tb~&2;_y$2lUVlN!vQ6#Iv)TjQC0OQ zp%N8y>%dA>3R|Y&G@~VIp{x#SlYXVJlw5c05(Z$cY za1*;3YCujjn1Vg$I~k@X&v0OkRvT$)bn~jODa~1);WQQJ*qsC+7wot~2_ELOqy`Y! zeaL|u`^@sH*PY5nErvu4GL6R}{1a`urVtNSLO#})?j=Dd-JSM77uhPTI?3Pddr10zT;^xGMUjlj!}xPqss)?Kz>+1 z`U)VFZh|YJP`XL33`6NA`)9|YEH|!815x%+*89SuY@oji7-a*=)zBy#h^`Jt*+BTu zk4LGkss{$7wCjCPL`u8r&jCrPNBW}yr7ZRTQKM4U*wnC;&Np8uE@izfPr#I4FfgU| zoGpY*spp6u*pw|btO1;|h58!MDch(o4^P=Gkt&j!DeotQ%KIAy%Rp3`2Vng`Dm!ai zFG^)+b&W8Uy_(hlRN1fAc5PnJ?BS4HNSn?6$f1q=oHz2Kj*e8R8QIZyik~hz_D-^q0T9 zJ2*N0?eP8S<-y76@j2Y_|ChfkgDBW17mbGAOmip>xDCy7@ZZ?}&jgF9s&Bx>wEFpR zBV!th)B_rGiHUgK;Ft!(J<&1mkLe}wG4&dF{{WdwubTG`k!kkff*6^4;D3=YknJFu zmTE7FlKExJryC~o@#14D$*%~Ixl#wB4us4W9(zM%HZf>c@5ab%seM6^%nlyz6(zG; zcOy)uZ3TR0pv=Z8_iEdI*$9={+puxo?MRQ&hE%e)u8SQ;_2WPW+b zOtVE#pqXu;nL6aY2WqB5s93^meNfy?qfLE*GatTbj|4iij8E#q&Mfgs9soSkidJ4T zdZsx!^u^C~@N*9UnswLjLJ*pIE4K=Urb{}1Y7|W+wN+s>oq~G;(rg9NJo`LAn%cZv z21(NnOk5L|ri-Kd0Mm5UbN^_XZM83tr|CE-Qx)DDqNY=RD?w`ZZN<`{nk_8f3#w*w zgG@#na5bIGJ^^d46IjzF;s-+3Y?^&mfYx-j=6^` zRS!fu%Vxiel4w@U%8{(Z11J6EN0v9d{9Yx3cT|W!mKEZUi&t2FKSFzbYRqvqA^G9i z2XscVfFwDdz&4pbk-k|zaf_W|L#>)d%Q{uB^PNb8wX2m67pwW5OjA{EkJYhGD-*=S zft@x~{KcW2Hnigj?z9u`v`KOgk9T@Ok~~UC+=_QPV6v2rc&eUSh-xm{c9nc>k&!AV z@6O(zUml!Zj>O3z7#s?1lebU8oC#Pj+q#03d_hL)y$&QdDl2^hsW{@ergE<*fYmr< z%_nRVbwosUA=%hK-w6-`XCvXD58aZCY5%9CwbSC-4{2~iG5|WrG@^l6dX+GDay(%> zZaG60enut{%dja&dY7-;l`i_ZIflx9#+~sNhJ>tl{ka#mVJ+Sx1HG z_9_)QGySVlXO1T=B>3CSXf`Wyp%{1~+`tCr#8z92!e~NR-co@q5D7o&9iAgL#rrQ` zjWG6o-$!RL!Hn>^mIy`F<6~RwH}h?wy9&2N7+rYM##O2VB3d}(gjQ3KhOA$lQ06Zjrwv9;4bTZ$w zlkq9dA6%$b3Od zbm~)MOD}cS($?|>kUw=L=xFs{dh4$Tn!-)do1T{BC|#{sZ#a+DYulDfbM z&F1=YEeEB47BnknB|HBI{#InfTa6i0{g_+XKwBY&9nADmRwP0)AnjV6Y)(l2oBH@S z!PRe3GNzkbP@W-LRA*=^_q8S*K;qvs^xv+5i+m?9KZx=l0nCQ48MNr`t; zl8L!D#w^!+#)yHLtackDL>Nsd4a-fXX$9QT31m}00 z4o!&k&#UjQ=ei{#lYzG!bEfz#!Z{&jyTS3DP&ga+%>)W)+L++H$VhoyjnOcqcgevx zC)v9wjw6|KJQVJ76cfe}%@Fx#fn(0s{_gIsZ)Kg<`?YmI!fJ6bEplG(FF4A-R~?}@ z679FdqdOcK;jp?NG2gxkvS>3(bTFnP^wKnu1<@_trbhluRBufnk7lB`g;$hM3hrh* zrK|z`0Dp?)+?Y=$2%EenG=%*gC*UTnB%p|yL{pNDX*Ls7L7^a+U4Eg%yl7@@tt$~O*f_@8l1;){6_Fyrz8ZR~@-ORpv$?{eXRB}{!Rcf~TL{Z4NhATk`+ z__ylRxe-;)7Pz%Q_ix`@3k>V0$GrsxOAiOdU(Uw&kHufvM?Nwz{!&-hfX07O>G?J^ z{!%RKf~$8a>Vv@5JDaj5yn08Yy7B6r46O^WzMG>4z9i=5cYla^nNhx8_2H!V&6b~dntq9qimJRsP@)@%M7h~pO(g7Fe>Ccr9 zSUXDp>=*VJCjO(ov8T?bzOu*t>I=QI$I+OlOM5ym?Rn&Pmek$%3tU=KKcertw4}Sb z_1;?2U0>t1CA}KgaBWG?YMu9%EKqO#-jbdbH(Xq@K-I2`OL|m(uXlxX*WdT%lzxKf zb$?19jqkj{q#r*mH<)yD(RqbQKOUa0Fj>PDCWd3a3&4@pdgVS59PRV_HA6U7gEU&P z8(o~-19)Ry>HC2kEp2$s27}v zD=eUuuf8ZB{psj7+N3H?c|0f~viNs%6ccmoy_wfx!pT5Z{ z%JB^mfoeMAZAuhU1>di$)~cZWyJ-~jFM2+Il9KI4m#5TyJG5Tv*)Te6iaRV z|4E@(T#|WUq>&m6PN8uHaE>jfbfzM1OET~B|lA0;PsDmPd{kAN&NgT&I z3KZ=c2P*kCAp&fmyk@HkhGz1j%%Lv+o550Q;(GL8DXonYM_sJFQGYtLlva^6!KHNc z{1aSC_XM^6Oz9HpmxGuBG=3yFgoGr$Hf1y=SzeLMR2EZqQj0uP zQCs63C&AN7)xM@Jf9aJKcyA~#iP#t{g79LZ2fT-bj58Y{z~mp={9TlUTsO)iJh8Nk z^kWfeDm&&{6s-n*!0y!;C$hLK&BuzN@GzC`a8@eykLs!X+~g3$NnJ%^qlc_IK;^Ic zrbl2)J}l48N{Lyosu49{=*W93PtAg*x8F`@2^~2sb`9Wo(!u{nknSjR{M3vlE~Vm~ z6wo9gX0F;VQ`iWBRR;3hM&J;I>wUC+8_8j)o$SC~;v|uLgrW36Me<2j$YdeToTXmQ z>>x?V$PRMdp^@6xW58413$Mq@JKgp-^oB#;GYAAjV# zL}uNKV^;vzy75|EuaUe`mS!V#lDJEhcdqAlr zU{^u4a* z7i*}DHZ8e_%x)D%Sgxb+&R9Wl8o?QGbrl^B%fIXKLVBSyFXbcIb+yDqF`yoB16fA_ z0V{l|>4HftcS{CDVr?h49$-XEh;Ufh;EYVy#K@XcH zwK~+H6JN>RI`Z}CC}LRwE^PE&5l%>b5cCvsRwSll*A#kqEoT4lx-#E~*KNb*jD{t5 zlDQ?OD_+YiN)Gg#{w_*TqOnxKhJlkpzyo8sbW)aUfQZ20LRL2et4d9>Rj3rG7JvL; zEGF;pM`y*-%WAGWEp=KnVaRQ4pv$)x_1P=e8wtr%9<%CV#6Og#N5jffC4h^3DjdEH zom-YAlY@6klANmnF-C*>#4)=`qVxwn`AFXQ!*WInNR~%qpv%l*rax<9SRqwNpo;3m zsevq{?@e@xm?T6x8VJ`>yG^(@`|-NX&t&~a$7koqhX^p zV@2ObF@csHmft`So(Yy_7Hp*S6PY`S{Y2(Yf=2m9nuiA^$%9T7%hFDQ;S@ZqOq1ll zm>48SKZ*bz2KZGp3&qmHiWEFaM?XSfK!}1Uosz5!7OE0f15EpO-$^sv_V5~ew31P^ zA5cRM(l#NFMcf6OW^~3UyMn=acqZY@an6}TsOK1=Bnb!fw`)8 zI*UWD+FZFISF7ZEBd#iz+iP?ouG&>r09Pv%76x3kiP!N~EA$@*Z`EeX4YyihtO;(l zPRfC{>Y`<$t*#21s`_3;)Zxhp?fEbN>hGGUsp3?(f|~k-GrC5c(YjJp0Ec+9^d$P@ z<>i@?nIfG)ji5sO+Y$O^Z(o{PE8q!j8(>2JuEJ{{3RXx~r{IO0Vp|eS$i5-DoAR2l zgq*aOhZ1sS4q#;Yz0;Da+9TZ2xj< z?L1;Q6@?H>W2Tx?mTNBx`XCsBQ(nW>a(KFPad0Zm9u*p@0_rZS9id@N0~~XbL$^zV z+uH}(LbMlms{44VkkMn(-aT_G}KZ zbw8206+8f6HFfCPz=4h3<-NlKe=%9jq z{c3e)q%TlyJtMtey$uQJ3)JjNNbjt>Z$5ZGK6>SYcgy2mbL4yIdS^CzKWVgNqjxgV znT+0#g{NfnH6){3E8dz@zLoVO4=$|Lt10u_!OuM;es^=$lJc#!^U+`_m1f|hhEl4r zsgabNZ@y3F(PH>g;Z6`FzxFoGKn`}$?zd5@AJRoOq33;noelKIq6@_;+iE^6h zoJ0u{8=8A=h#8Uvt(5Vanb6FC6}+P+nZ>|6s^ru15Uf@Fm2hYaR>L|5Ywe3JWP)0~ ze^O)uhhQEUn4ro*bGCi0Pab`?eOuc^vVBLJ{&dOqZ4zn9weOJ8Q?7mIm|8m3zAF0u zsrFSCoNyzxp6vrS0=1OOp=hIO<88px4Mi!Xl_R|d=VsLyS!$hXTI zEl%UZ1IFD3K8PM##5fwgQM`vY-icPgRNoSQc(^3=HT$a4zfD`)(bN zEjlQ)vy#MTBlLW?XMYGP`u5E!Q73CMBD!q7REP)?qU?GtBK@135!g4xfZ3oTl+MLN z;gT0;18EwIBN^D0F@@Eql*0lcH)(dyV2Ts*SXR9xI}x8W`S`5hu~#VfXKG3ln#paQ zx@Fvargseb0Hy3DSI(O8?LuRt^bWnPT?nAE>&&QnZoe_wHnhtf1>CtSfYZuBZLJ2C z%Db0@a%Ky~0h^tZ2ss%yH==#!f(`JfR!amW8Y&yYbEbwW zP>V{evw?ZS#A|IdF|1Nm8vrW@om~u_t3&yFwgznGdRYV&PFJ-GYz}(Nttt@0{&LxQ z(yrbD_Am@z1x|vh>0=t(L;H4dO!(k|J;)KgOUic$k&kIM5*vx%iu8B62YWO8{dWy1 zfrd`Vi6QdQr%%egH~fU&yg|Dr=`qblXn$rtitAVIA;kF12{4}|%e2P8D_lKeEk7z| zGn~yw7W7`ia-0MN{r)@R4fsk_=j-R6J`K^QPbjR&0HKhuAd6BF>aYaGh*gQ&Qhx^r z_diCHsa3BK{He)mHRd=U?mNgaFRs15yoD}#eF71{LV-= ziv%{|GnHGR(}K;9(BV`ZopN^Q9eb|JTo%3#p3jHMP4 zas!a1K!%{A#a|nEUNZJ{=j*1ZRGZa7t!XwsMD2AoGp!;Av8Aduf5&S2+0WIsUey|V z)dnQ-{sLW0NgSj9Kp*lbj`I29_z{$OjMH!+N7UQg7ek_nF*k%nb3Hx4BozOxmAX(Q znrhAQq?y#ha1u?mNZg3ViXH}6qKP{LVWN@4CV+`HnhIv2o#=XyCX`~xP48rc_V!JD ziM?0w<_TcpUI8XftZDHXdpz;mbv=FLk=!i|>zMA2GGAzPcU!BT;=CK;yzP05Dj?tLLVF}Rbg+yG80brpd`*$!yw^jDD$a7{RbQOijE3QWH?+@q?6cjn~ z!XX+WZ@hn{Y7kt(k&|Rs#NMhT=JzQ{E~e2~9gy)|3kw2qXdTY^3=seyC<+5a+^TikQ45OdPh$ zjRtJpHZR|8iHw4cjlqqeF9)tdcvH0s47 ztZ(^AoxIE9w&`k~>IHl(4iV7{_pjrn#OVg@=tPb@kNWVhZ;}1da!jx6h_354So$KblR65=?0> zOC$DjN-Wb=Tee(FO7SV}?jWk0DagVfBs41MpggERzH7Tx9bIYjmp|akEVp1hHfI*G zinkD!;+&$CWPJaDWN~rC$&GM;8V~s(`-#inxVCLGAtZ#rg&E1CnM4M;rBR6PaF*~g zTL2OdBrD+X@T|+9*Hraq^v_Ag@5d=uA%35pIIF7ly5B6)kHhC?v9yY#tw!j{yY@eH zIhC5wh=}E$eAfnuC98+Cj2|h>FXjxr(^o=FDa+B-4x6(pV_SN)!=H1e^a+Rgy)}(K zULT@t`6G86R=L#PLf4Bg)J0fp%9CHP?iVxbqb>_DhCSa0rarp?H>Lpa(cg7G#GPPG6hR3nKEA+}FawWs%3P2R_ zn>j6`s!U)AiGXK3k>vO~`7eb~P^l4OtD)U(^=Xoh<+C&57zK-m8SqQYlGfjIyAlbM436vM_Jz$Osrq zoVS6^l;av(Bz!4l=j(jgzm=@k3+j6N`9=ng75!2kC2^FQV%j&c&mGjB@bnOb=u zRB1=7s6Gp(WQP5JFq$k|$L{X#?yDCs;NRWd-O9gvd;5F)|F-wy)r;r*uU@`*z58#w zd#_);dhu^)cfCz@{}e38*}v_sTvxSmU&znz1B8ZOiB2Al&=9~^c6NAod$Q|kHrWxs zZVb11@qC^VURInRiD%{g>cbKG9n9|`&Vp%_lOQiLqMsVyvpkEE34AY}M|lh{M0%g~ zxh}+QsG)$m(a(&iG5+~M#U{LBIZi?x(}Y}c3*}0pUi0!-C1sKnobs>3viNWt4UNJw zx7qx_2ud+y zcQgyl3$Hkr`h!p9w?kF9iH%A}yE~J$*UK?mvI*r1RB$c)xGz4`T$_04aewSiU1yG3{)hR}iPlM`jpDa}pPS=H$ zNglFk1b5|KTTYQ~qGY-5`$byw=qFBLnck}n*CUd~bUq_VezlFRE^?gn&GrS!#ppf4 zWJVMIr(K;MzvcIQ{2Jf$KlRsgFr%t2tKiCjG|8jVCAK`y%J0Rc!*)0IlTbr1q2?iv zx`usQeRd1vM#U6Ft#G9a>z}9$mNHaT;8mi-7)LWUvGUvTnr2t*6QTD1o?IatPoYQ>i|E9z+vW-7}x;j6+%^c~hgKBqx)~Sm-t=PW7#zcDFZJ6KkGfQa$ zz_nPsK6CMd?kS{#+W9h-!C290z74^N<5ZcETsc8Qbv}}oZC4Rz5Y;CWWF*1?oR@&@ zug@&UQ35v?u{=tuTO7BkF0-$CNZ7T8l|!UVK2=%2A<1zRv-{~HU9kcusn@TN{93z3 z=QXn^Bwm0syK^MMXq|ea!Z;TJ8@*_eb>P0H?iTqqfcbXL+FFzq^sbzuJj20FV>-C5 zaj_Y7Ee54vX^nH(fdB8w>->3sn*G1!G2`j`%i{|_|CldXN4x)b|MjbvFRT9F*Sk;t z-v{|Q{PE!Y5}h8rJ079me@FfyR{{SN&j|YTX&~PIe0+X!^8VB;@Uw^+=4B4f&Riu9 z($pw4`1)%V3v^9Tit~t@hIi8_77;(7c_?D&n(&z}1P=}g(br!O2EYIAp=gZ!pT&Vh z7~x4mSsn%NXjsB+eEKxF)P$y(QzA)8C`wTIA)4_o(Lo%`D{;{j-xB$S0$#`R+)l-O zXqxnvWOrGV=OlsCywSi6^Z;&?ATGi(i#tzad72_^TC^EPL9Qmm2+*M4XDc(IllIL)UW|fEpze5Qz$eoRD0U@VpXm zT|^|iBQHicO)JNjD#`l|*SB+dZmN`aaG>r>6i{z-TA({DvV}u(T}&)i zO-}j4CArKa^j~}wq4(dT%O8)?+54k!k>{an%%+3Ei2;MC&J(2KYuia((k#PqRWz?S z(^l#=7<{jGv>;=IoT0s91}90L%^^|RXVbQVS&{gh&B{nqP(0=sE9=+kADxVNicoKk z$}ddTNCGUtuQ3ySEm@XgWSV#3_p+}&7@BbFLq$p+1E>#ikx$JAa}^lFWUy%#lep?4 zGEbG?LYwaQ5T&FJ^tXUB;F2qBFkJ(SUq99Xh1(#uf(5GiKZ0fFU@9 z(Zgp)AUmThhhU$0E)D&#dSy}ds)blwNeDQ6$-Hu$B#4q=ij#=Ve8D@{FqHy_GJ+XR z*fzQ&h~*hgCh;7xVgg~O5N5#{WOq>>O!?><>YFNtNFrJcPiaWbXqFFsbimB}CAZ<@ z04|+m)TClb!n$(gHVVaE9Je@*!t$JqCdN5f8RqJ*zo)B1q4uj%Yu=}pS{IC*NfzM> zFI%NuMZVAIOoUKwp-q~hO)^XKd3BUUW`-Ess+N*7UbWa~7zeG=5dADhLO+?vF#011qHA{;5TE-*Gxl&Qz z5=}l%n7D>mjSU$UEJBPJFn240^~=sckr<2DG@rKjuBBD(o&`!K@^4DhA~sh%xJnhm zo+1W9xZh!-1_b$MsqDsifPKbqtP8CRp)u!4ghz3i1PRe?B(7WmbGS;wzA8sx4uATj zoK|DXmkO>uN2eDeJPhCv`5YQ0yhAf}lY(4#`@#nKeT^BP-3GqFxvcJp0!Z?wPx|_4 z1ou4ZToWdY1O8@*+sDLswT;G6#&Rj5>YAYwNo7^rG7B~`ahal|uS_+<^rufox+CMV z3`HYAuK1eXa>cXkiI!e%>><{pi_$Dg@-Z6zg~oY8Aa%6znVRHP)Ue(nHBB`&snNPzaE!M~?*2!((yVn2^g(3~ zHK)X?nLN0pu+and^?H{TiEM*#Eu-_}qmzr@&JHdvetCa>^yaHgbCW2RwdFQomW)8; zKGR7dYLiCMKNw9?oKN2jfA>S2W8YpvKMm0zfRFX+1@b~$F?b6Dnj|EEog_50V)sIi z?fK}uNDww(0_?6@khvxmj0g)r+yN$pq-2p5$#OyvjS(b~=Vb);ly9D$l3S8JL#;iC zJdY%|wGOiX<|KqTkSCIOT!M!eO6H#OVt5{6F(E065&&AA*Y=xv%q*2c2p!UNE<}C# z_QJgCohi`bl!mtSf^<3I?Qp%=$?1-epU*3^Bs$v3XQ}!2K5D%o^X5u#;BNT!)p>Z( z)Jplr9smfT4~$eMF(dyN=Q$SpCuheyS6?y*ME+UBDc+8MP+{u0t{7GYi+<>YzlP$H!1**znT0 zEHBl&oXs}Sxyzva5=3TLyALn5@>UkGi>%5c;m%_X(% zIemY0{M*S{WxtcqkSu#DW^ZMpoXps-yMOmtj`M=~I1DqsO@(Ljk59v`8cS#I&o3)X zt%Dtwl0d9Zy#7SW?9)&k4zS)jrrizeo+G}JUBcJcue27#1` zB=00gGdxE*z99&sF}YJ%)*7D1q>N6^WHPUIv-vto=NFu-`;tJZ#923bX6@K-mpw1N z>#YLMtdVqaeE##vp<^&D%}Rf3%qz=DB=HO<6T(;BkbFdH@R?Wq`cX3BgAeV>h1VDV zh~DSM`|$o$yp+{q#`mKc1;$xkzv)&f1aFTwrr90N!sWDr-3B|oHKV_1M+@}Azu%W) z?Crnycl}*|@5@k&y4l!cS$3WOKP}3gi}KR@s;7n8T&SD1?`fG{ewkicIsZgUcl#03 zV;L2Wg}ZG+Ui-4Wyr%RO^{98BJoqyHC{@Jm`f!Ia>7EjfgJ4R6n{x`cpUZ8{1 zBlOGr^Pk?nKY(w(J4T1+#|M|kM}73LrV$DvOh;5ph%o*Df}$0mLzymCrb!!jT%w^+ zq@gQ?Z_5tC{&X)XGJWztew&gwB^iLsEmKEV{>R>n3jWXj?#n0qpNIMR{r8=(2ZuDB zXVGMuqo3d1;f!pflO*s57iUNRulF_zNWzGB5^|*-M8WAA#bA*GgQ0C!?|} zqf;PnszP#H!e8f<8aqQ#&u|jcYaEx4#ot@zG~jjetdGCCnb$MwdRk$VTe&$U|7cTf zK~(Y~yxVGOm%3+lJBM5%_006)_VzYd5i7U6$vRm|1~=yA2u&@q?nYRd ze~a33TuZp!R;EqEYxO~7T5+o?+YtajqZ1&eu!$wiGCJj(jn;Y1z-U(Os#_qkVIrN& z`oe6awnFSFuVLzN54|q6bcwKHuy|d9!zmYC7C{bl8mV+aK2qiRrhxGF>w>GCf9j77 z(Tx!!vs;oK(pieRRsHRoJ^zKbyN&*9&wuUh4v~L|Q+yr8Q63TI|5RKPNyYym$`v;fuhd7g#2pjb-d^MMcxs*n^7(S9|(2YH1yuiAt z3d@GMFzSJU1{HrlrZ{N>3s}cVOiF3hs?oaT>YYkP;OWS|rDHegyUZ}xNQH!|0>1{OP~Y`~tI zYOyq;BAoxw^sX%ta&mSoT3ujcc$lomuOuYh;H^Dp(Z7&hk*JxjLe-L4St9R^_C#Gx z_;&k`(!&m|gQiXn4|mQNzdA{olL!>|@N#yHw%-dHD^^_Fhv2t)mH{y|7kHMju19^;!9M^L2L zZ-g*4cdI6g#^xEsu|(JY4EJg$#WGf^^Nj&1+Sq-(#cF||L&nl}5g3Se&wtFzIFU+u z$RT{U0?D_};Xv7{s5eD_Oha$Kbjo}Qt<>rTlh<{-$bJ#A0ZHUbUz*awm)+@Ayj*fv z68!-{j&jE?U?FosI`n(qIEd(pXRH|WQp#OKO!EX=62YKhW!qH22)a-*A`3osYv+))UdXH zHD71R_t)i7qkdf9CZV+SpT%aVw`sq~E}V&24Nkz-vpjhWu#jIi!nWSZnpt3=2Onte z=ueno!$~%ek^eLo#~DCxK%5Zig^zK-;AEg*0wKCWOLoS|E#1u;dW9;rf7E!bs`$y_ zBUnlueP-etZwe6_%4X>Cr;yxi`Mg~j6AR{Ry=*xaC)lvL8}GZVa{HaL)rKtk;JH;F za{MoLN3LW_wm#?eL5D>B(y&uYVSUzbpizmrRDyO6%YwD4noKuf?~X0 z#lp6R8Rn9~mmaGlaw~60*kCaG?xF(L605O_%6XiYr21uN6glAD>J&zeH?UMgkJAd_ z&KS7zuk>r|y$|MNQIK2UT%VwacLT^S8xq+$XV(7f0zmr@{f6AFa@_c;v9J`puCs!v zZ#UQPyTZv2{iaU8AY!(;7$@n=thM8Pd^tz8`v)GXN@votk3rS1^lPTOUdUQdO-vFn z>9L|2Qg{m*xHFu99%BC_Z>+3oWPT>Kc@HqjDwfjYbVj%RuupwTvi5~q-=f*4D7aZTA>ITVm`f)55{5iOmd>ZKqH@?waB(tmD}aIc zXh>BmNee(9h?uvBuVqx2Z*U6_;hP*4uf2fp$IILr!&Hpp4poO0%iietSz?4yNr1Zo znFLep_9i|noba4J-Y(Wl8pYp?A+~Q*E)nAIUwkw*V0*Y>jeRhum7C#s7C#`ze=OE3 z#V1q{z^LVx``n+jfT;>ZU<~ zx>+20ylq7$#+z1g;}=J>xmom`=h$W{SN$LgPb!q;U2B`xg&EA)0(f*=lbLf^HJGT5 zEVInm*JDPa(o9P^Lh9;?rB`2CD@X)E4JENJVb3ALCI8e;t{tSMEvJ?9Sf`Tm(9OBX zkOtW#85v=T(X8VSDF$CqUjd2tzw@SvWlEX`d-aiZ- zP{pIm;;Tc^0DetpU%^qNx#aa&e?63^H2a{`IvHJ`6?^^wVX3hbeX<=e;|^iRCz+^7 zgh_M`ekxv(JKmci?y5_*%H%ZvLa^TMC+P}Fv$tgRWM$!`Ka;K8p7m3brXHTzeb)LF z#|=^cZ-((|ulq~w(|X(Q5G&=HKpxv2RQdC(@cGW=rupv6xb@ld^Xov*_e|X81X42O zBG$mDu$GP!E_BPszN5q8Aaq;6pjqCqlUWSHGp_d&5yTOzEz2;zLeI(Nru!&TIba49 zi*PK?GRWtm0bg^U+W?-4u!(noJR@gzCPW~y*{HZ+fo=BiS@F`hs=R+Xz+GIM5*&2h zngjrW1>vZdE2nK3zYU$st6Jv)wr!l`JU?N+TdK?`MyAvL?BeRHeyi9J7Wou?^hP~U zeOA7xv0N^ES}gLnh7;dEGVV^u-M7G_nzjh~Qn|K{Ac)WUH&rz;2dGQOK?&rh^X|jo z-6n4+UrwrKIxN5IBl>wo3Utnq!gIhu8r9rXnQa~?+-s+$bR$oT)A)ssY(-etme!J} zo})Hv-txWnLSAzCQjBZ*%sE-vadGjho9igsv6hIe;rYFpnJBv>rA;c3|HuQxg^49p zV4mDnjWUXF@ywJrt&Z?XB;Q z*}z9%Uz;nxYVptw2Fpk8HL&-@GDnEq>4cYL`0awZfvOCpM;mb~2@czW97&9L?INj% zV@#b(VpV$}wX}H^lv2C&3V0kCE}cvUPJSMIOKQSLQMX#@>C#T3jaiNI!5`zp5_G9! z=5=U~%`U{aAz4A9>!3!eUoD)Q?yi_G?v7aTP7qxlg7DfIC+Df{B6hacq%JSZKW#Vj zb@$yuo5s7*fbv1@O13COOp_d^*+~%~AVLO?(W7x_>T#Cre2#*RJeC?(o&|az>=o$Bk0Ug6fC9?r7&o! z`V!>Wa}^1ygR$?JK}HH%D87_|5j2k#)S2KX>d##bT?i;K)4b??Yefcmak{WJkoe&( zOyDuI3%gxfNFM&;$8F^YlVuf;sytHpJXYkOKizOGXg3ni?ycXH+^R|`i`?6!!g$#~ zR3&l%jr!{z`)AP5#?#0y_OA8U04U)Duf6ND9@lQ`4QP5^9H;O#IoNcFpcqk@>EKv| z;2Xr{yx?#*42=#F(#tUj*MOUu;5jK4CL+q2DeQ+%(>Coz*In{evNhqyE@j#W0mXLT zF_ul%4ctH6qGyL$Shqei5W!9V(p=5Ity5b!H#xQ|wr$RU2x2GIs^zhcuWy6q(8AZL zk-hK3^+{vMQ~ee{_tyE)=XBkepas!YWou99G(?ix6X3Pa+}_o}n}cf%ci?F4eStH` z3IA*N)PUgWERK7k9*Y08?^O)eeHgci{)_viH3g5hBu?~=MqWA1AnAs2Glbaoj2XfH!^em>=B^Y2vQ1Wlr#|+F;c{> z-v(QVY*-^y!8z?0xdzNK+jW15ntwvDerh0MsgkC=IXo)fL}RhZsy-dSapk{PIj81R zgdXaJORo5iRkm3`<4nr$@!?D`$<`$N5q_lQI3pO{`(WOd?QExclk<4}n&CDh+Ac-c zse_rkN`b&XH4K=xNzKVE>X^Dm$HSXmy9D6VhWLy%#1cN3hc{}mtuveNWI3C8f6+B> ze@NKeXLUzBH8p1TLw9K#Cx6mYE8@AdPKx6{KFQD?-n^O_0}a93zz;qG{8-@?n?FyM zE;JbR%bs#q7?;2Np6{$)KUdhJ>t*!1s06XoLrm}=l2%QpjEtya`vA?dXNKv+42KbY zX7h-#!;K(hKLBxg$TsPqm6rx_DWy9X2-;KgNCN6XSRhO>9gd^;oEgJPVImDPJF|Q& z0~*Il_fiNs&mFFWToNd9h4#T=17^&TOO-l2bmp}YF}%`4@gt#~keub0a#~0#ysg@1 zWt(lm(>1$d4OOk-v!V;XJ81@I#3#nN_{q8lLSXk}xXiBHNmGvNKT2${yyEgurAUNV zoXSf)w3KSksNvunJiIvI$=^7$pxDzMtoT-l#+y_BR3Zf8C= z0jzF3N=tG5!tHi2ga>va7N_()lYKBr?GTq4p^$AKy^G6 zT3-N-UVSS<&xnKOFk=v0(p(T`nK?rT0G-`E!hI#d;wmJ`g{AI87-+` z4wHl>%m8hP>JZq;@;$~Tyu^WxW(8!#_Qfm$EGf@3DcbuNt~)lQ*$F#h`iG&G*#ZJX ziqH8V{UYP^I`x9FB(2)YRL+Nco^Yl!Xn{e=(@sTm`eJpt+OWZ0@h{GFMGK>7S{8U1 zw^5-KwM@;Lyc=21exdz^0k>)1A|b3qk`uA!2fFzc$EQC&+2adY_eHc>)R>?x&i$fl zpX+OqA7QTi{y4d?W29xT@?S**y0(nEUwBpUTtx5r3dj9i{}R-f9g(&j9Vv8N<)!ii zV>*2)JUYW?`kAmSn8x^)@0E~j(n#=nSyFQeNR3;g<9@nYwo;Z)sg#I+q?}=q)Wkh8 z-Nmu*SsdYwi@8Tx=cMG6$XC|Nev&uv+WNStFD&v;5GM2!-pbf3Y0%jKN+r#;jo-=P z6bo{eMGR?+A|&k&ARuX!#OROlA7jG1MU~K*@mCjs^na#xpZ$=E!!SA=G=LmT&`XNzzpbE{kJlH zyPE^>KnK_$1)dCCF!c|*DUczby-yAZ(Q{_Q70@-QCf3*!cg<5VOI{^bk(f(Yq>$uO zZ5@TQD!ng2C0<#re*)_ir{fP(9YI9eJpLfBP zT;WHawZ-#U!WGv_DJT8_**lMq=X-zPpQnH_wN$yrR3O`?#<#JQv0D&Vn+L4DO} zL_ws?NwIlr1%hw1=lGwSX~rm%bcld_6q|h0M7XlI8^&!tq8tL`X$JAgnBY@Ui&$>I zG^K!+{@Vs5@27}DYUfYwg}EI&q#nCG{YFh`sh$gJ9#1ds%iL!LiD+2bgmlhzz~iK; z7<^k`S%a*waUNp-My`Rf(KP)iCYrm%{Fpa zQ8xR&hFiXfvg)> zk`gblD3bsEyYxPhbl5YR_?;j0Mg&6h!5HP&9=kM1dN0!g=AnYKd(I=5aO)Y#U9h?@ zMxZ&uS6GRL02uRvGTCM_HXb-B8UnvvYldZ8yfjBn8+A#fRNy~yxTfh27QW^XI0uAJ zgOofdlqD>hUk~;F)JJ`_5fKbR$9}1lZN6!dK7TOt<(3t9SeoG5-Q8cdg`1{Q&bMN< zwgo!B`Y@RgLL9^TNexR#fJJI zFuMZ`YLrs~PMT7$og6Wz;cA6kZzUun>U9+q`50+2mpa~KV|0^V!KDe$mN zz3b{i|9Vk}V%2+0;*{>B5;5KW40itIMwa4M%hh@lE>$^f;omK{F{7(_2{d{-_~%#G za@Rx;&|g=r|-#*bd!k;LuTaQdz*-M6Byg=r2R$4@)!{MXqGm{T^(6)`*O6r1A2bk9|R z=H$Af<>uyQUZ7@ImvF2DBvdHHV>VL^FBZ?tk|tutDoi&TMHSU;wLHAlU_%pLi#Y4V zXN)&;TSV1nAV3gW2Us=z567PM*XV%tuRm1jZ#ZV@Fhfi@t(GBf)8VcU$Qf+TUD4j#;HknYs~r~qEl9apVqXJ8`(~fFCwhD=(c#s(VzkJ0rJ1U zzXXaTY?LaV*S!3lJ}i>Cux|>D*g|v4#;|LG^>phMg;v*vNWeuF zud)pHH}O;yhUa8gup64E5qP-({DKhxcM}&H0Y|bIGV6TK4Uy~RA$9<@3vnFTB&}AK zY&;ox86nRYk~aM^J;X1ndQT&%-jYI7zp4kNl%O2aur!^XRc-=dZh=?~`wNJIhwV@^ zz>Dm{>{Vv~S=sk>FQ}78(DN$srk+XiG+ZF>!e~r^Lnc(8I(|3?xr(*6Jphd81IAxw6?IKu|b>~7sJbVyEJ_|~}Bmn-s#XJ>q_5{okIxIce*6kIeflf-4 za6dY#dDMc{-=j@2l28}v%B&ytNobah0_k1r@V#yl&my3c81pmqE$plK zQ07e|@2BL?bD1iQs8wW;B4j_6IjMSH7Pl{&LopeTCLU+2(-^QRX2mima>AJ#)?hVE z*q=%X7HC#TWaxJpL3J1|+L9xSe;m{zCux}eHj~p9NOC@h#ni0UP}y`Tfb|!97Ap=? zc3(iDV1v#;4yyeEJ>dzfF;HAI^s@?HRakef>KD|A2i6LIjo^$--3gfXoPWf2b*(V! z)4US9JwNGH-wSS!+^BTC5*wZ_ncPTgpymuc?4?Lhv{)O%m^w35)%}(i8oR(*h~;hV z&{c-;TA@Fp7sWB9>4jvrNig*^J0UyNv8O~LRvJvDesI-(- zcG}F4SP)QL4iZniN67TpY@tiz!+B?H2q(xe?~Z2Z$j3)_Di1%6@`^&i>A3grrHl$i zEjd|%YC2e9u2JTwt)B4ZGARNrQCAv1@EC`1>` z@<|#kA%7n5kaihY0!!MKHx!}-5o{rn=BPw+&$zhBHKmZ1Ne8A|(A4CZQ_bU(n7 zbx|7#>7^OjGk(xGMLcex^JOX(VhX0N7)$vy3dFJ2$4nagO#j+u&-hezQJ~%lDmtH* zg6QI8^3RV%`|Ogc@x$^k$DwG;Ch{qgQVooRh#Bd&nyya^nByozVe!y!VanJpIAqEg zX6m~7{4{9oXk9t8Xvr@^mB(h_ zjN8^!f$ccuI@a1Jr6QXeUNY3;4A0a(JIWgHmR0fU>_p;gcHO;>uSaO!;q@oUK_BW8 zs`26G!0^+bz*Mc#CcCvdBLytrik-?T9MxxZDHZJxZHQW0^A+|8V~PEeujP@EdbIZ$ zZOOPnY^bK`+{l0{8qzrmd!AkxO>&zXD(7|BtDuqSa2kSBRb;HVxpT82b$Zw+j0ti-HjjmM=$^@qyFoo%5Y-Ym$zKg$;RuK5&k2|t(YY#ac+l`AQx zb};E@I*Y%LnXWKXmLkfbVszJ4-y>zNo{LQ_e1V}1U8U@b;ES6;H~nWl>Sy2yV@~Z2 z@cB-s?^Az<;mv!^J~yU%l6l9PURsghg`q2=II2UtTCtLK_l0ve>es_r!f#*HEs|iDAENF)o852lB<0exezDNjVNZTGuFkG9v?qP8w%-b!hHF zDbfRPp7b76fjeE>_uJZEJ+b-|cYZAruL;yW1*Shf(_gd_-Y9Is5sJ%(AIaI-B zKIas{aHmGUO|ksD;)Y?rIgZG|P}B3sJG6>eBP!?E6|#z=nk#lND=p&=moVA1+9ISp zA7y^DfsZ@VZ$Epd13D^GD=bqeCz#3UCMXK;((+d-Xr)slK9B`5T0~l)KI@twdJ2Y~ z%Cq>&18Xs^hLv+TCjc})Aj_r3BgNt4PXVF{xQ;|4#~|xzGIy(YkFFd%^rK~!JW+i0 zElq5WuLc3-Wvd<9ERcgg;?A2=Bl`hb%<$o%HGW9jD8^D9>yTk@=9fZ9joqc*2H$xS}zM<`JV1Q%t?KV8+3HT+-xS!I*X}V~iRT zM66Wm)(tVlhq&2SuOj7R*t^KdCat}c83TU5KMMK?wvl(tf6bziZT|$ClZV~GFISMy zA$X%JIalKs3F3e1U+U67{mTWOfA;82y!C8-Z>n#qAHP=#I`LnOx-4&}{CI#&0^-rU z%ZY&xf>*Bep|h@kQ}G~2k$5} zR7DLeXmaRX>=zRkEE`?bd zJ?KH@G;(c+ZBkyCZA`Wf=`lX^>@jW};FZ}uK0H)-ZASQ1MekOa0C!lXM~L}>PY`g! z6n8^t!-cAvZ}H)ISfA}1K) z#j*A_5|^se&b$)y-)|-|=C~z@&AH?GaTS`9-T4hq`ziBwS?yPI2I!+&U;BK5xxa0# zZCg1qt9$=kScfk)y<})v12EFAxMfuY&;}sn>$!^0cG_nTDW-DPn`Y;?wytR3IFJI_ zQ<@x(Pmd<7&MUg0BNv7{@~%Vo6I9@AwEW>JzvdpGb7g6ep}3;KbFK;p7)-=qGw&fu zS#xDj8x)O#trc$<-nVhHB9z3jXBoW#sZ)Bm{P6{{Z5+Llns%RN`6u=8vbU-zC4R9j3fJS9DaRSK&Xxq2EIJ z=PWO;GV!|lD4=%V%<0RC(xemoU=zPB97ECPt7m&-YfI(8@3MNwZ*#^D@}vjwE4H00 zAMt89O(^g`L~O)D1Ro(F4;#s4=PCW8lYXY9H}}JVoA{4}=b^8%(P9L=nl_42zXl-N z^znNbuZ9TIq(;bKnHUE*Uql0+nKD8K^GeM*+0_5Im#=EtSP|0&0$?qBO=;)(%E_il zgZa9DKNgP_ucwooLc(ya-Kw!zh>z1fkqDC|2nE)>^18LjR_d^_*%A1$1hGC2e6OBf zVcL{V4dpJ?>o&H$y3PNoLw35-mXT{B8xnYwG6k=&ZN=g+3f&7wYC-oBY;hz8y)Y== zrMJZYEAZu%w5Xwf4k}u;HBV)H&u(#=FkhZaaMa3^Cguz{eh)a?0D>=#YH4CE1A(FK40W#;bGeSfG^{fZvTpGwJPfG!@zm*yWg_Ei1-DC zY)my1n*qB;BXlSa^gXjHTVIpI@~&-OMuJXb){T3pp$i_D*#{MLbB)Z|2N^oh^ z_A^bGy{=UK1#a&N>Xhxz!=l3t0RU$5!|l(>)JRearXy%@VPVZUb|_ z?takg$pu0E9VAC5(7+#~7{|fVWR^8L=pPOz-ac)nIkN1B5*}MbK3o1^4q(757%WXl zQ<6|wD*(w7JWKhufbh4cosfPi3IC=on`2B+Oj%KYbepPLu-oDSn!9GIY$tgMbRv<- zdOx$>#W7rmerwLZaN3+Wh3pDX#XT}I{jAKosLEs|9`&kI^R~#&5UQDtn_bhcANLJ@ z*qqg){^>KdDcTC0ENXgTqYdMl6xZDw6B{2G6lzvzjL4_1*NzA-NV2>9{kk`eIhcqk zOCVd5iUEy5N8QU&64h^!t_u64%qnJ~R8oFWagQ}*axzXyn)Q}GZ3m5>78moOL0Ysp z#RO-k)^w@r;pFYGEXd!@hu7%=EK~&t;eq=ImJ&&u^dKPHjWYC)7j;q?l?V&1FEEu142EKr$_ zm!rP&0H$yz=10xnqv=CEEX60bV(o6xTGMQq9QBnrm-4k%TR$2fT&^Y`+}w1%FI+=V z4;^k}3aB4M4>X$>8!?r&Vg?Iz69(K)uv5)$zUArDJ)`4AwVvndC6F%_1`cYwQv=^B zA&=+#qg`kB+nWhPKeo5mtyd*}Cpa=_E+?gUP&1PRWK~G&esdW)x{7HgJdr5DU*XR| z#Cgh5xE{xi)4#v$y^VIwV+Oy{;c*+A>_Z#mCvUm2WUO6YWNClx7!qC+7vZR^#qNDk zX54WxeOb1ft;VZ=T<)7Vb8}tvaN}xTDz^)t z=}kSM!rBkW(X^Bm(*YWn=^9|sClq1-{{J90lGwO9lEv=m&opG{6Ku?m-;Feq{>Vxq zG%4*MU%5ekj1XO(jNuQ@nFBZYrz=%hb}U6Ra4JNE(mY&bViISRe(rYX!G!%=mf1z=~*!cxYo&B{s$e`$J?X*?fu@3QyKpOjA&6t^h7i6mP7#k<^BskpF}{>dwbRjP!%qBl|UYZwBvU5VseSw_o@)@>Ay zJ!jIDOS<;D(LL>##<;*>e`-pT3?PcWO!F5HV*6yVR|BTpXTv>xif6wP@5YmC;NM#+ zM>`ay75m@g#lPFQq8q*TcM4DptJ1fHJ{FHZ#<_IT48oN^5hk+Zp%T|?4ef)$G|yUT zjOUt^DH&_n8%IfuyW&G?A+W}Uf}y?vHB`~2bV#^aO(auy8aa$Yfe=tx1geZfNjN5- zcoJ<9qG~l>%Ex#B>6tquW^aDsn9&YX(EmXhw@MD#>FXcebjh9d%@nw0tfykxN?&7|Pc3sZ8h7or47f@4rxLKnkg0MZYk(UK z6Y=oVWO&0?F50`@gW-rlKmX=^IvWSQi`{ zeZy)4NSZ0L#1$Ipx>#7at)!vfh{YrtYq5T_Ue)U$Wjs=BAW*~ygOXxzb17*2c^Rmt zZoA^>QawLvgTgF@03>s4Kks8sD5LW#W`o219b|L$k_-et*hIq zNWWIyNQGNEvL6L)%335nfZyc$20USMw!!jGE`(ll-dow5iv(^Y^F?$t)>)!j0w+!GYkhLmj=G@TkWrLryvj9%IgK6y6b5B>bfpI&X+`3lfolso3kDxN2gky#KBt0P>HC&MT zDFZjMPxa4FQ;r)ko7uCwxf!%#jGLQF^B9-6=%Ej9d_%D7>nCg$LmVpX=t)=nKs<|b zfS1^?0c$sh%|;3XA`9*(q*NJ;;z}*kS(0Wzmm0hux0h8&bPXLwkTh*JJGjk=WNKjt zCz&qfFxFxJJLmivQ*FPbhMpuxLmc1wvvEmf|5`r=C&xlF*I2-2dMNkAGvK*=aJ~N* zqrtOhX1x`slsQD;e zBae{{$bD9VI;V-cDsnm~MvosP;_=GHNsnW&h=-d&rHM_Um%Gp^7U;I%#XInuWzeb$_tu5!^YNZ|afEtnfY0>{vD{Z7IDH6N26o z)_xS;VP1J-_);aL&M~K4Vs)NGS^SZs--K-hIjmm(hpd|!q=_6RzPb{P!RI1`JBx?b z@yx)X(i}izHyS@!o6$EbAWNKR9r`~8nF2%iYR{1u)ohbqSCUbnO|LW_TDkWb_e*6< zl!Hh_>qlpu9w(>mJA|Cfh5@dQTZv8eX@`7wvDfve!pvd@VJ$TakJ~am0Xu{T){Qo+ z=G3a}kgr1Isrn8T@_LMBb3q{w5ee;XoKkw`}+zRC{wLH~jSlVjL zeD?$OZFC)HH2)9LF6Y!Ba}izqi5@*E>td3YU5ariQcyo#tKFYy@LOK5mY`h~d9yZx zL>&;`W9FBb8K+MN)r?Z^efUjn4s3ASSM5=}${1xf_v+C+l*NY_Nd@bz=K>tPHGj$|-r?bmyjWjyVwqBu=1MP7GxzZ{+t8Z9TT!5h^R?>x%u12=9+_S>IgJDoX;U@B~?A zjgdjQ)r*)o-dN`C(*Rhh#kcMgs*+pL(!3spqL8Xt|#?_(H$Q z4Ny=tlp(V&NZJF1mBK%(<6$5@1*#iC1h4fAKY9{zIA*@|Gl9U@i|v{Opxg7%#mU7- z&-3kjQ~L+d# z$*e+9f6D@+_5(yj;>G?&;9I*+a*uNr4>!44Gf< z6sTHyDtz<;gBU8;=crwfa8wDZ3aAPjg;CjU)=-MBkbERpiqN=r>%d8;M*?Je zs3WGz%&Xf@hoeIbswj+%45MG!^Y6<0=EZertysNi7-1f~&AIDydm6^A0%S$@=;Y}4RCC5s%X3$|H{CNL;^#6 zf}1r!HWRKh?DSNllQ@?b4Nt^tYTAT2U9ysG3=Njup@%bvRPj91Hk8;9@Q&g*LGzWJMcf&`60Mtr7=6j7*|29(BNwGsFf{FOhtUODIAGlRd%u_*VQKpD8$sxX0X%0oKDCQJW+6qOzv_-&x@ItfHOhqQEIj79kFkL}u69*WPJ znFJ2St!^SF!>6~zRs5$?zmIV{r{93C;QA=RNtyqOyTI+`dCw?-<`HR@21LlE({&*F z<3do-`b7|VW_E0+k&_RS<`rmbck@_N0o?Fz!jQ3q(*nx=4qZOGXE;> zmk4gI%o@m#_NZrcQ|N*{qyAA*UB0!GeDqvz6$qUoNZs#>rrw?i`&0=NwiVcdi#O?3 zrhoh#G+PK)>_1Y^_?@lry~nqhC{g9{3&G8tR@P8_gpjkQ_|3=|rFHbS`wtDA;M)>m zxH5AaT7b`fZmETRuL>if;2B6j$3#fRHw30T{~$LW-c;U|=@y6!gZl+e+{q5GXhEoN zWiN5>nyes?P%7hS2o7ij_>1LFQjn~<-OK$KYy15&>cG`lWk#x@`*hU=Jd-0K5AdAN zZeVv*#&LPw;6(1oIsksI;1>>7ACHp%5sCo@G2u{W+KFU4?Xu{&Cqtu0l_pLJXDDkF z)Mn`UsdZ6g4XGg)iu#nU#`>qMrZ5;2AAam-tgNQPxcp>;RA&*EqU>j6oQY|xcHkwG zwXY}dDRDM}c%k)7SshMj1^vOj9%wy?BK9$sU>i7hVM>zy8p-@zG6KlP zB#&@d54pAhkX}#`2S}6@ER8MIVnk8+L~?0=Cq9VTd$eF?90^!OFNOPzs6wq%ZUD>~ z$))s=v}HpzeLfI)1GK@u?2i;Naf~~m4RlOP8Y8l{1t1V|W!mjTGfcmgf|u?JH?_Af zBN1$PNtgxZVUt94xT1}2>0l$MHMPF883-Og~orD_Y_J+xtqN|+wzH(RjIeM%U z>mp1uF4QEw3RGr37gSt{Tev0Jvt%d7DXfe&i91mv7N_<~&BAJOW4a01OWyn!#+oVOH;P zX+e-jLmBVv*=Anb1G7v{vage2n@z6Y6mLJ0T{TXhK(sj%&xkjlV1<%y=MLD7Oa~l+ zd-9zTT7U^wiB&ZWTYEV!njwgtVP16tA!jzRFEmp3LjiI=nWJpN%gPE>TUNh4RL}(T zL(nM}#nojr!umjNzt=j>nm=75JJC!Y!@|`iSKT_^d0g4M$Y;&?D=vL%6jpn{`Wh(r z;rp>kaHLB+YL9p@E(H76xKmzxjlSz;j;vz zyfrY*2IqrD!MlsDA=3HRYaQXDrLce%=`ZZb1cg0|?Y-#tMhS)TbUD_PE3uwaFdb^w zRuEWH`h@gi$eIC{z~{`~0fjWvwQUa7MlvlSdHOTl^tuQO#|P5s8#@D50EFmcYLc(o zkl82!dLEy38Pg>eIE420Am?s{dN&Uc1R_zr*8*ITAkN`_kr8CnfPG)h$ud4!Vw9IS z$Eb(jdrn(tAB1()bd?jQcLsk-aOL(ATcko^d#$ra@u^S3*H|+U&63wW6UYI1MM@FaZ#3#=# zJ!+2-^QSoWM_F5qrZQzjMjDHcHGd_tU}8N%aN<0Lc|&{&SgukQ)?6^O;%68LQ-?L{ zUTd5V%LerJ-JWNx=YeIzw;(YBP3KQIu|H06=g}*0HXmt6KI~7~6$9o;`7p7DE4Nkczg4^r+Hw z4J*%I7#*vkI1j0G?Bxw=n!kp==t?@J$rza;A(LIAxvr$O-x)*wOL5Yh^JtC-;#2G{ zOFZ!NX-C*vVf~%;@!eH_ei1h2^6cLDwO->`^+)nDA$$+f0;Hpm%(na*#5iAsa|I3= zwsx!RpRD4*kCC~)l!Y@UL4?u~Ia!NWU?H)je`9QcV`R0wE&!XNi1i7)sEJC|V%?O} zyv8c|V)fx~9LWc5a?F+8z&zXb|? zb_Q*yG>+1DGSi&Q>XAnmiP?4HQc$llKU!qo z&#b-3l}3S&*Gf4JFC$64ea%2|pZv$DT-z$!-F!L{*=I18+~4q3(3qhX#(0}Ko`b|U zHM*nlS5p5KWp5ZCkXJ`t`8YI|?arhSfCZdi&bOKBkVu|CJg>rXsqbe+9CbPRoW$Yz zJ*3&vJVnCOYbx+Fnt1O#BaeyYQ8Hm%>Fp=C1D}NMHApkfi0DvhUAyCWt>er<=urq5 zQLqFIh4EDaZ0|aS(^W#7>)7P@GDA8|8T3s;dQG}?tZc~pT8Dk$k_WuBl`#gy-x$!yP z^LY;QP{M-mJXcJt7gLVelTM1r1$;j2(7$!d%$*ko+qyZe_W#OE^f>Wno>YI!HvN%z zB8A=;6J6T#M`mWIu zwza@pxArqq`b%tn6^AgoNE?$#f~BeXVh-Gif4p@(^^Q+FUI!_pD+)X6dfO-Wi*m8= zge7bf-xEdByBdens}|~;xOsS~?H(+Yd7yIqTNE39e5spV!rM~T_*WsLc;lIG*BB|Oo5w8W)O%>& ziU63YqTJ4%3T0LLzLgFBg|zCM(+lCj>#c|cUO-ZVV2g!0$py*a2Po*j@-JRnmHkWT*(SMM0*Sk$#^mu=g&ZL>>Vwr$(C z(Pi7VyVzyhc6AwF`gz{-o-@8N@*~O4*gN^L*IHTgy03|J73`FrdsluB2xReTC_iI9T+@3`at(L2W_YDmaJOp=in`Py}{ICM+?8 z|MdH)OHhT75ILwI*e`9YPXsRmXKPtwg`;o&;`8&au0PjLPfyQ<_Ik|T%W0!%&gQJ2 z{-&L6Xg%H#U0oGf@N6_54$nWaTJL|P{pQvc&6-#PySPwB{|iz6VA#l~X1R`S2Rt;W zJn5Urf&%E3iBkW8HulMYt%;t{&lkG9?5I5#3ARVoZd-szF7&(%K0a-8Ms3h*l?i^} z$D43JNj|Q>$yh6#=c4SSjo_SQr*<&#^G3*g7Jb8O{w#G&Ugx~|{7B;3QH{U@1t*7A z@BN1xij22Cud!KU~HabFe4a4}*J$5E)0vh^p`(TIxctiH(+)apG#b|4m>X9ZzLLm?z|An)mw z!8hSpf;hLtW?3F7wjUN0Zd97Bq!M)e7SYadzs!6Gf>1&@NB`7j660dfQK%*x6ek_% z&Qu?|Mt~-C6z)!bL_`e>oYyW6I6F#VK#8$x9foD>Ars(qp? z|6_lKaFWBEm0xZz(p>%MQ%I5JuP9CNXNC|aywd)H4H=-!UcXWF_N5DdVw|&YRscP` z3sZo!n|VPJ^PBZc0@*X5mxv5Gp#R~wvDmbqhJiT&3Ir=*b8?J7RyPU_@+r0)SdHjG(|DGptE8+ z$?s3<1ewn4ye@1-$-cl8#ar>AER_wX6=+}mqDSvYEoy+;guwy3rjF1jbBbSGM@kYY zZzPW3N93qa2z#v|y`saiJh>a4om2EjM7HR4ImIUTkPB`dWLTNuuQNdP7}aoXXlFGu zs?_CSOFhTud5o|k`IBPRg6}|4Hc4Zc>mg?T=NG#9BM$)#(1iyT;9))OqSm@BSCvoD zZPuyjlMH#<@|=2D0i^G%hJ1TVy2C;0i2ve-|MKLh^Ske0Q+2d;b;QtNYS5W;odEYqo2~^x| zEO=${RQ#pA*^#MBoDPP6&3Y10qXo-|spKS_7}ShJ5v|$?tl z&^OL>M=D0hvcUpFMd&jM^)zJ7F|O^=wSf*d<}-2!MCo`K_{03NsQ34>>^=@#*?0fF(D?q)RJ%Y;3vO3^(ZpF^sp6chI?uZjh)@t3yHNTvW8XpvLFEv?P5g3FQO?P^A0SLEbqg=m1uclPLG!- z;W?U;8$w$`>b;-?r;MBP#6h}@ylJ1=#*f0eBJF$`w@NZkA@Tg|9s1230A@TGUC&GG z-9jF4i^{1%*rU|7v;}q3yRfKAmjT?$`xYA2nY7kk=)BHC9QqvcUb4se-piZIOaJKa zXYKB9dbbdam=~H(lYt#DK2RITS{K9@r;nYl4|~6B^S;i9K6RdNZ$__~m6LDZ4=sW3 zSmcw|;e}tmucrr(%l!!Xw)N=bPNCQmqP+_Kx1f)rDDs-R@JuBP+uNm#w77X5YHF#H zev8ltQ*NyuSnOPooG`HIWS8Yc&e2=SCuZ1o^7icK#2X8KmPWXQ_o{CsZd`rQdpE2p zg!giP$?&t#2lKA!CYW4$PhXq9eAn`}US~-6<&4@R&=j4&1pWV>cQ}3B^lonG=6wS= z#J$(Nilx2(kYHY#N}#jnwWIq5HQYM7SVOdew~_G8SN zC|Gc$bu;54(lzth5_}&M>_CPn4ygBvrh$JRKGXn+Ar$mJ3cgoc&izLq z)A(hs*HI~*Q-bVHm*EKe(&}IN;u%f8$RrnDHk=mY4|1HD^p^w8p8xd;jsu6X%DRSY z=8%O~bkdLo&JY;ziN9Ny_Y5!pG?ol&8pEMcZt=%)%={O<%r5)?R5q&Dnde=V%yH=w zE@90eIM6@@_Hb=7GfG=mS$2o^-oef1V$BhKVM71-?!7K373OrSQb*&A`?Gy9stX!< zOV>+$dB1-AwHE*K{&KRf`R+*HfjR7>uQEs6FvE7J3Y6qp!ObZG5{~Ko{o?DBBZQa< z9MWxRrd$wXTI3MA_Bikb?4Gxc0?*nxwytSh`Y@t`RTkS8MAZrCpV6`98j#Fujy2;e zjJ3H5p&itIOaJ4jkjIi3Js3|$C>$IzcW`xW`+EL)6+3WE$ONK>;E2W`IW9Op>PW2>RtB5&MJnW&@%R z&#G%6V*+!ovIjhDAWdh%TRMx;+CfDy9w@Oa`YO!UeSXTU%VK0sMFxaAcnF*@XAMGg zm#JDDlp`tu5U3>l{Mt4wSP|srBA~@yI4~IcS;6yVp_KWLB1LQ#=V7NW#NJb-33z{k zgovBYw1-n`e}SLY5@x%F^ElTUx6w_tP+}n;fkl$mH7z=3b*u@H4ZVZDvUh${layY; z*S~$WZEW}!oPX;->OCz#1ELDP;Ln0J`UXd~@vfaxC^F=ND1o>kMnWj3+W{6+CS5MjpPgdape>L6Jb5@Z%^a}Mjn3K}dv zLKFfJguC5ciQv969cGvcZrx)eG2VK5#BvM=V4cJ6iDTk96Nz3g28n(5m)3iIQt$jT-zbFuxRNR~jM} zeggJsQ;1zqQQRa<}j?49uo0Q~up8S(R2AUWb0mWk4@_5_v!=33$WohkB%%&w;KK6iDdZW4B4xICVoD&beBWuJ3FPT9rVR?2 zibm+8VU^BK!AOMQ3`ShISqrgytF|%0Ykud*Hu9u(r-vXp<1Q*}>kV89DaRDPs#2EQ zCpgM9;l*!BZOaY(ar&g6`lUZDPeFBR0A`>q?>lAR@AtxIr<(6BKtc0Ou!nI}O=B-! zN%>Us;+|&&cq?i<@dBNokk`qp1A(k4C8XsIlMO(jwu|#B0HE)Lbd& zXwqXuFVoT;NWBMLzGaLokc-c)^Iy-l9QL?~~}!r2BF!ts0P5`!dN?OD2Z!;SLtmH-0jfaR=tHP;Jx0y8@l z`7f%V!cDpcC*i*PXmXMNkmW3%$I;h<#2cODhSA-R_;3zT=M2?99E#hWUQsY)5+t#< z%!6ll%Y^Ztvy=~0$s#n>qqU*+aPV?;^U8w)nh%;ollrue%P|_aezZPj5tGR&6NsT_ z2o1)4h0>j8xmHAv$2b?8{$-vE3X>8$wGPuX`JDM|9Lm%J+iHBVS?%=vVbH0Ydusd@ z;1~1A3A~Xl^=dHXsj#IP7LKwqb`^+DD3j0-SvFHn7imwWbMC6(A0uwoBQP}lG&(@~Elo8A@yAPAUF{RwRkC%fFlE}>Z&wKQ5X~Dvi!+8x z{EUG^NhccGBsoC6T5$_fmA@Ph9bKhhE5rD19T0w2dCXN}IE5e6@Xw&3xF=D&!r9xy z9MV&HxHMqPbSgaPiX#ZxG>`mBwRu{j@hBf{X|=7f)wbSPUT^(>o`&aVYyRiGI>n7A zayvRruMppUCoZhb(PaV?fKclAo1AaXb{ACb?7OAw`9sxSA@=*h$Nlr`LC+6y_WL55 zCxAT?hRQBnS)<=cs^2wbd_=4LS1 zLnTt7jT-ZbmU&i9Zpo2t{g&VvDQBaJ;eQ$ZD`f^_&?23C_-WjoQ&ujvC#uI|(nE=Y z@PRax`@0)RgM$2cBdkS`ZnvXKvPHe-P*V>WQIFVCDl(mtQO4R5M6D?vAhR4D3iUg5 zIy{)Oy4W$@zqGIRBQmYplO@`^91whRHsf&#shG`BSm|CaYGF1@lk=Zy$rJe=)y2c} z<`M}*o21&%k}{&;yK63#&ueD5+wS&Yc{lXAOQqfe>i_F&;dN3>{V4^UH^3EH>at-6 zN~ueR@RDtNI0M*P-JVqU;HbGQ5!wU;Oo2Emqgk$6Bur&tl=(Jt2=*%i{A_(<&Q4LA zT(a_6xr~;qDs&zGaJ!frV$t^@sxDJNeu;6Wh02i>qX-|=-4a8ZNcBHiGNe~{m<=UJ zvI4WUCEsg*eYpa1YR(}BD#1x^^0s{cV|2m#S>V7uQjZWV{Pdw6sx{ZWx@7Vyvw!k4 zKZ)kFD>+v;;pJz3#sdR&!UKWP;j#E%F#36II{5?5*Ewiy#Um>b2hWELazC*%SfWvL zkwOO`nY_m!S?B<=R<=w*sx{8jb<8Q!$+}Lw-Xk)pFI#TvS3d<$F>>E-X_}yJr*>1`}K0yI~xS2(_IWlROuof?~qs1KMmq=FD?MG8% zeVpTWG5Y%ZUK!O)2afIR^_C}w>27ObOw?4f#i-!_GW-ye^7r;v)mY%EXZ72(3pK~0 z$kM1VD$=O2QfQN*K>6__@RctmTLA^?UkQJn{)xzRg!$(DuG-%ul;qqz+z#Y8>qU*F z-5UG$!3l^qli*s1~P8d+n?l8FzabnJDIQYJ##1>({{7vKd?Q(Z%r04!~=2 zgdr+@vsB?^uLQ3C%-v+RQXo*Q$VLgkq@fw$sz~fL%T&(z? zFA(Q~N*@O1?oRKU1v{~_^$~V*^mTH31btb#IFrneyw!r6{hjWwPbLS>ZufW6hrTQx zAG#ij-tM6O(e`&qRHiK)yFWR8Ee;n}W=(Z^;r*AdXPJE-OH9LczA=olzP?PRL~)~| z0PyvXxD$*2`1&LZtuzL#YyU#e+z+KM%b)=hua0IzX+p{`yV>qSOOE*OVlU_}_`4++ zg~^JTk$VH3>=*_}^G{An&}dy5PO7b{H31S}yLRajjFyxVIqQ;5nf@hz|GTIA+d~fr zH)bstD&Oe3~#pFYn#t5j=TVgQIQ7~*CiC=ornWXU*?)vO4S zHf+V9zti`?QJ`r}U{G>Xogh0T5u@a#iuw8c5|hnGxG=Ur)us;0{pb4lcv|~7hN%Vw zD5zYNIKxboSv@tIF6YR-TNHn@b2$e412Br2H_FVP&re%pxid6al*klm+N$Ck5k0gP zXHAJ_s3rshUfUX|W3p{5jw32r&# zIfkS_(t{H0KSN-o<1(OTjG2Aa{qH1uHxRExy|&4Cqd z3xn`P1vALYp#etRJQvje^86Y9c>b_|JbwXz=O_Kg^Hcug`J-T_OrHxuYD1^X0X#qF zKc3%hDzb7j9v;HO(%3(U3En?wsbmVk^J}34c>dH|lU6Vi=S8?8B<|GVaypXl%~%RB~Hl?GVC<4m)CMs>$^60jz)F{%E3NS07kwHOrZ=PI@Dl41H~K(??7 zmM3?hd~n| z!3|t@9=$nYs1Zb85&H$y3BtC&N9_iwGCN<^Gt7@3A=#e;aui$_`qJ020#PAmnvkisMu zFM#cuJFTQ|sJWc5UOT~I zXQ8&wEeAQ}wU;nGU#8f$x^amI9DA1TD9Rw9qnvgSq^e{TeE`UjS{)2@Dts@>0r7pd znR|nu<9c$a9LE-S!)r-EfU;OfssTLvWbPW5Qa_G@-}6=yr7(&K(aduX!X(|CD8iXo~)(Wl~Wm4 zP>3;^XZ~yI2Sv1(twj!)04?qoVX(d*a1OJFRlS@bc;Dv}KM^Wg8rbg7#!?)=H3HxN zW-D%6(U}0*3NOC@WGkd4i39i8LH_H#oo9x4hdk=ZbP5o7H~`A|Q>M@i-rmJ4^*x@o zwM9>-C5Z5iVf_uyVfvYT;FnsmOtRqy{y5P0PtgT&k}in2t&@uv&j4Yb{#jJ?FfbOw zI%(JRePs*oZV+`3O?-#KK*Z8;BS}%z5kBgGfrOt$uT)oGRkm zMm_S*%bT^3UGK#?w~EnhK--%)+k8H0Q0{$J^?`#-e3nC?NThzuo7l z6z)lULE?Czbhu4an2SmWXfi~+HYR*;u@moF2V)Mavboy2vQGx-WKM8Qx0BvehGGqi z+7r-HHd65;<%^DKg?iyjiFHA)rRnlxgCQz32KHz)))tl=i53ueU=l!>9PBA?{6$sU zhcI1Ac2y(SU^&{@tkYhIVz^J-gYIr%o?LB1)VXzGly8webF+7qMPj>gO>sY zgaZT}d5xk632>+7`BK)^jnm|V;*Cc{bZ zZPPzKCmosk3;UoMjb12*9+>^PiDfkO#{~^j?2QdTK;-9LHS)=I?P%?v$9R5Y>}bjY z#WJ#3qAV7!+QdD|E%e^>>lanvLAlsk&Q_{=_W#O<%sDpwA4!`BXR%minc%I4oVw2QC{(lalL3k=-TWty(;paoGPgq?GfLtiyXS0@BAsP`a zAOY}DsfMak1^y$x+R8(aZ0_gtVcJ>r5i<>+N)~VJ3n1fxXN)bFHkGA3=f0&X)MaIJ z1YN}IxK*fp2%+W+V?1rZelUh!%}igY<556~(9r)wp(WoMCKPjn);Sa53Wi^{xrT<>B<<<`@@Mu4f1pji%+JL!`Ui53?oa}l% zT>r)vCHy~twlfW`Xev#Cf0U(l41sOp9q4bz_7=*@0dFgRFm^5%0KuKcMTk-6o-7r7ahi_EzfdBry^c)RL3q)jg=_gb3X}c5p=_O^h)+rLhY25HNFHyHI|+YQOGm3 zy5n@S>Q|Vp(TLK;lHLSqo76C?P<41fc-4ZO%2&R^)O=JO2p#&4CRib0kD>O$dB(%en z!fA6`Ogn*ZG{jy}f-Mz10`6J>f7}bT?#|WbmaUD09ZBc#z3IZqWLV~3w)1=u5T#n$%R-iNUEciYkVz%Ruk9Qam>-H#jPm7t@_q zhiFei?RrpcM9VcSN{}U=YG6oIkdY;AVLozMra=vt21*J}-l*~poa90@P40I|Tj$eC zz_-FbtHwOfw|~p?zG>ulpI^`MzuFn`x4JQ7YOZvXsp~{~>K@+i|Ee{vU-19B>;jg7tdZT6y?#j$`%km6X<9$Wi5?mv_hJ|3XeEE)YAqg?vR3MSM_O-D)6r z1RXX!>~*-0hovImDJomP5#ZOS0Ulu)R;Yki}jz>A5pw@<$%UWA%|LBRA2N=PBn&-GRCDHzn{j zQ+WBY`*+{s&Uap)*I%uU@0;tZn!kPjEX!YgB|NPvB{$;7jOqLl`JIDt~SNbxs3L>hz9PQP7AY<~03~cLeLZ)=s#Buqmjxqe^EHN0?gg z!>hd{%6`CBstVo`7nqN09)zKS^#B?2B>|5qcv{4ok@A+*IZP?8J)1EBR9!hV5f!cO zw4g7#&)V5vh&!EI_6?-*lAbU}+-nTGREiTh7C=>EDqXWKsBIn!n8C-F*FD!yojupJ zpxFQbk}1-`%kLzbYX^*q*ry7R2to1;w-i=ld;~T0Tlh@BmTxkpGzId?$r-~Z=mKM*elwXQy?jAFdRX zHmpSY{Z~&w{jhIKeMY}eXA+>b@dgP?KuQV0N`R_AEB!IKYjq5n3n(>Wb#mU~>|Kwd zIxycc_cvvPaYJ|n1p(GtwbgHqA!F&E5Hle9z{hew4TVECxwgsj2|;@ zJGXD`+SSFH5=NU(qn0L;fQI@&a^>ulESf})L}|VQ!<+wv@Z-S)o>91!cwVREef-9L z#oekBOu~Qr)*LwWqWtW;RPe#QMhmFT_hyYASVctIeyNQX9LC*ot=pX*b=PHyEiobykV<>>4U-$kfO7yI7w0Qop$$C3;TPlEUN>Up@euRZ?s z7Xai%5IR-tE%8#{zg6vTTF$?l-tUhddj0}TPCMT|3ui7sF$V~UU%7ui^-*0u`A=QG zdK>P*x(yQ{Svi14l?JVBbL{PHOuj(`Aeg|EVnq7!So45Sw+3g$2>{$H!h-s~3!?h~ zv-E|eagaztKC=wUVaW%i5m1FUaX(pRhe;Yy!ZxrX2mpVi3JnQ_9ip@G(waf6yJKYv z^a#3T^(yM00g?byv1W@y3F3QmcQ-yrcWOb#O545#7FLW~Po3D7bLgdiD22}J9u-+4W4 z_I3xOPa)qYW&P{klS%5uHs+L>b3YnO#$*a{w3oGs79Y^_Eo1nyJVHq%$=+-qooYrzqAuZCVOe`Ul|;; z@Tl4PdCU$U80mhc%xo-OoZZ>2_M+09MpWpP9T4}M2I;aJxas1_hTSai)%k9Hm83UT zeFA_Ri^VjW2Jh_Qgw=)w(#t$pK`nSFK}tzuYWlt3DG`ziuLLeQpH7DqIs$WNAG#PG z6bAloxMRcthP6jJZFZeUtMpE6epB#GD`(x;BeZzFF0V$UTZ}rV*14wF8zCsolrFmU z;omO=wZPToQ}oE&`^hs^=$D-tc7=*UPrB_sMsB)p^me=9#>~Q5QKkOZx*WXrmF-iE z6Z8-pu274PRxi1U9aD!EQ5t8=s%i#TYgbm#Ij}4yi!8|Ss72$eSBBoT8W!O!lZWh> zMZrJJG5ibUf3d?`=5Sn0b>gSte+vyz_Z<>sMf?s<*|fFqWha>||%>ZRy8Ju^Oy z#ZOJ#h9Hjr#4wSo!zwW8m%NQID~$r>J~2vRcB*MN1hYFDj~ngVgILpL3VZgOnybpX z45`@N=zRTqj`bxew}2Vf>bKJn#HU$GBI`r3hd#^o8 zN*3h1uei^7*TIze*wBOGZ}a$JWO0{+5fDzbn+Xl>XYXOR_gA{?uf7|;c+EgFqW65E z*!pn&NNkk@a)=#TS!pe*VqPMUj~Ly#bVf#xulpll&)+S?X##kT7Qap3g!rCRe2Eqm zf*45_2fv&1M9SGe@Kj96lGu>Sy(@eODj32v8X~)mvRFy+mPt7Apqv~6-p{hfy+ps++nb{Dv|VU(a51J?Nz&(Q;k9I)_kWtI8`(6s#7&Z17Qu2C)Z@_3Vk?>UmPPrXo#7%47Vr!d*7rroZwq;JEH1%EIv%1I^pl_XD_X6mmE{`pTfQ)ZBZ@Mt zWUGqv@ZXiBjXx?Ij|s=d*ut-Ez1WI^l4_BjD} zveppSEb@N9?4VSm=mXy`lxuUt4E)5;+WII_#b6ii`z?O&PO_NGL%D(Ut12L3tw2jR z`7$r}_+lMnvGpG`1GCrqRD|0t(MNd~MIwaP=o?Xw`Z_jsQuToo>(EO2h132>Z3Cvv z27jW<^?%Jmx<3tfuA>Wh2|`I|vk|SfC4~}7|5e|Ism8J#A9TnvRoO?Bn?GfBCj@=; zoq=Z|$oD_4tK7wL(Ykbc>jlQ3A2l z0{Now4P;&b0P5GZ^?chcpadnrzX6nv?Oogo)VrZoTEG$_ za!d(2*nt~3ocIM#iP(e~rE6x(7fXvqA+476P{J+n)*EGRo$b6!c# znpdbO>V*m6_mt$jvf+Cl7H#QomL|OH4>3=*{=W(Dq*SDS`E#qP9^YwDUlpwp@Swx9 zeFA(p2SDty#Zs>A0@JB@Oui>Yx=j7YAP>IpAIu-WQWO#*<*?TpAY*Zh_1lSzG!tjV zC^WtUcn?)5Fldm|YBbkR8S(Qkupm4R1&mC_#V&UzWZ&i;2oCqBe{GS?xQuFiOK0Cw?gxJ%An>je&EjU=^X7kh0%^(dku*uQSq2{ef-VOE(LAijHcG77POm zK+pYY6I`uBz0;QH$l>maQG^+GQaA7h8+PK~m%hioiSl%?SK>3=eD=&ZRI z*td>X^2Wr220kSarSuQb$S+8PaL zpEl}Td@Ze#G^3~ zZQOqtoKL~^9NGPzN(;QR!2gL+Me^%Cf~|2{axqN~{ux@?7%|DewpXT>;kP9-Hj^s+ z!|fqFf(eOXgP~3M)}frE_xzjrh;@1VZ!B1grmqq%sSBo|5 z{r;gMlgV`>+ZvFpCP{8kCD`ZC`C}eRm3D!E;d*V_L|fDd1I)Lxt+EIG?XMRb#BbM^ z>#e4pcXpYF(iV0v1WdO?dz!4aAb5weSsM)#3v7wV;RYT>V8U|FF{9~L5G~7V?V=O% z{I#!Ew1%hr0;}UB?e2qpC-xB5`;ClDX1q7i3X1DE{{Re^Qz=ybH1RYo>G){>d{|&8 z?+!Wr(^i&SO#26S+#>~Kiw>jToOIk-&Ac=9Cqo$*sgRj8VB_fNFxN+Ls-+kthjDG; z8|%vJu}Y6+F|`fSv@rpH_8}^~9skm*+8R~`gt@25Z_xK5Y-2>4 zfX>Ekw*^g~88}SxmSOUw8GK{iHgho)c7}uN$#T;})C>SEZa&~y)K0y+kuDQ76VYW$ zo4U;CmR6s>Qb}PU8Jm%Xy~sAe_tROk&VN7sy7_91@&-w!y=Ntdw8^w=pKoHF8odz! z8BW`drh|xuwd0!JZjAXcGiP1^XjUIIlej2=)s1_JQN7#lYb)_)uUE63i$3dA+AfDV zbmT1%d_sV#RXg34lOjkDGM#Te1+#d}Z$SWW^(%lw%)e|5va;jEtgl=WXoDjE48#hn z7VHWy&pRc*g;~VxBMQ3oFU}>a!^}iie2{PJ&(*5@7Nfk$c60RKHP-YU=IG68mraS@ zcHCOEEqn`={|-)XJ}Bk4B3sm~o{(I>LK$p80Y2l5{Op65-?En*HJ4KNTLARAy7wJx z)m-*k6SM~YU1yZvn#)exdYs=nzr`>wcGG!s&Z*`oQ|;eDo8#Pgke2B`%^d$Z3s}9a zgr)6&&dnuceFOx`xpR_OyR{#;3spBsi^Fr+Nt28_4Bb+^iUDm2iLUun*Q4bwM+)%0A<@>zV-Ww?zuunO!6U@{OI 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 |7!G3II4xk}OF31JfW0p38VBvNMSdqpQ zx+{lWGiF_HI`%=|_TbbzXR5BgkK>6)K#ni5HIgD}^?#8}C`|;F} zhV8KRvT;|zsIxm$Nq-8DE%04vqUEbMe_Va@hs(Du#s2f~&u*bv*NSO+*HLYml^Yh% z&%sD?^^K#JPgrv_fFam)ql^K88PnFd9%B?5?qTB zzXFc{C5w>{7KO#Qxw*Nin2fj}{m2dynS);;rTtW1@;E=dev~i?{i>YIj5$J7J4pMQ6HF^Kkd)0g0k9JONu1;pEZ_ZAa z{N~cpS{>czx%4zXV6j-?*+BSw^&6O=#5>|wh|ymRfFo1G@?u`5J*#n{a}6o8XQ!pW z`RS zoQ~E@ZL*K~x9bvAH9b$1#}zbPyP}mjFX|l@G!9J)`8&u?>D*___?-RIEeMB=y_nU$m(C+ST6tiCL%6ZN32}D*j*tw)6oL*=z1@> z`91J{J_&Tn&^b+K9Xucybq+3)`FWKmsUQSFi_(*xPD zlp7@(-6N?a)H!d2~u%jaa{|tA&@xL25<+>C!f`bgSVe0Yito z$Jv|P94^k;XO@+icqAxYrQLs?+^Sl6>uY z*FDE|(yia7iI43h&z@#a1Cfw~nj%<$lA}%heD>#u0YHK`iIQa}Icx6zQj5f8Fc=JG z27{UJ^Te3Ge!TX7X9}w^8Kt5837Di!98&wztZ@mDvX;<%7qYS0ycP$66iF3njXiXt zy*9J(tcK6)W1+~LQtRA~Bf=qz5(pVu0h6-^y_V4jUR+uu5FRITMgHI&Q2B9U$52BcZtsXqf=VF=yBxp%DAma*ooVB>qg>9R!$~s1Gs+ zOQjtT{U}EzX@uJB%JuK^l}d3ag$ZUmlGY*k7@lDiq04vVU?XyshYs+CpgDhU zOiM@FA+gB<)#^_KZVFlA#zMB`m~e1TwA#?uJ&m~pYf`c^Oq5WK0?JhaJi`J^LQiNw zqL4(OoGZ~9Cj}`--S{B!=*P=emVcun&7wZDPx1*h(rfX#)G=sFMKaQNvm_K4+$7PM zi7ePi7O|vdj4wQ&9!Yz}nLehl$YT||tr5O!uN6-G2=fCg9K z0jY`dRLei_jS2}7>tjx|ty^jsoU3q^BH>`>GB86r0QFM-4P#1e3C7*mcDJ-;>li1a z_t0a`bfltBWwf?ep>!!E>-52}>fY-MrD=GV_i{o#0SO%#_r}(ZO-HRf2)b@^zzQlw zg!j4RSaB>m(_Wt>c;`KYX-};uG{V7@@B~s2I&i?Y4Vs`ig_J@X$eW36ydPj1dgzjn z%=TP;p4icL?Zd;JgYO|)zcRxO=m#Q}s;q5Y*f#7yHnj*AxE^2cJxITD`SjBrCXn(* z9*yZD%Wn`K+@8Z>ID>RZ%R%{FclUeA7hz7pBB7@wyGpBoux{sHX$I~YS1~WtmvMy- zzpLa7H4>c`^2}ObdWdNz{em80lR?o1zdDGwZpXI7dy;@qyX|A)XS&SaHlMuaXK6& z=+8cjXMaUI-JL!317|q!+1SgxbY1h#RjLxOC-V-SMZRb94AVrwil+fpyOZPS?ray@ zlNkFWVgT7j`ivLtcwLqB$t;ly!ZogQ^MH#avP1`s{Qhn-Lr5uF!jr_WzrK5y@M-R~Xe zbrcejS)Jv=)!CiZlD(0e6CQG{{Lu)quh3{H6|O;YgE?^msr=FYB*~%2z@)^bv;ZXd zNAAfbI=ghy4=0yrm)p+Uv#Xz8y}m+kPcAM_US6F&zeKMt(CMp}&(5ySUcJ0TuYN=) zFaLplK709W+r+mfA7Yhj2(-BnwtAB$(sXW&t7)qB5J$s_+<%91PmZ)snTn-23LMCr zt53m8!}0!9^@KDbVW`d*4L?+FMf!)%JfEHCS2i>E>2|x_gS|cYZ@1em{Hs2@f;KYJJ@tRaXO`;y*&0i)+e#?FeZfIA>azvCmDCkIw@>P5Hm& z4X|_{3;2I`=is2g|9d;#-Xs5ih-U+3QfQ71ZJ^8ZXaA?Ig@^VT+|e3P!jI6&`N`=| zAclcFoAzqD=;I2C7rMki`1=NiV^q&whQ(L*-NJ?`yHXQr6;d9Di z_mM@d?PAhx3K>Ay1l)}_5Tph$B&(-P7Moa!T_Fj zZf_k&wt}W~d;~^%sT_jpJeJ7yN8|`)#heS3+yR3YhU@@ImNSC$@gpiGp|I!LPW;hxrT!V2TRm-bO=d^v<3|MP z(r)bljVe_y<5DWOUYi&Ob|gnRDh}rso<7pK&4+qyLl@{1`eVWpQv52blPEB+Z-}Id zBh+)uhjw`$N0GW#6+Uf0WBCNFnTM5UZ)T>uld*^eM*k) z`)nMuh(t+Ba_WM*vc9^=d!rPWm<8$;a^axLiVK+)Dt@U#tge5a4F{M)p^@Te70Q`> zNL;kpcxtTC8Vqly<3&fu*~GZW%RXdfElk+O0d+0p<-Vu2N=T@vSt^sST^haJgyaOK ziBiGUx7vJ5hLOTyjN#rHceM=kN3d8+8L!kSj(u3?qAjFpE=OT`G-vbd>Az5`g+8H( zMpA%6y>6aOYlarF1Z{ywjWxxsV$n2b7mi~uzZla>N9Y>>DkX%1+-Ekg$!r^K zDvy+-;|gLZLbRziIN0oTy~^#V)Uma)pz>Wfo?W6F~^;HnW-2LhcCWr=G(AsvNhf)4jPixTqEnRATs=u`_ zEz=i++0Hw);ruzP?U`0g z2emERJVf`ho*o))6mTOwjya1tg&UcvpO4Ap5m3P=;MUWh1VfT|&E`zy2lA{WYxc|u zGM*BC%EmF~1i63fc{{y!w;~~VJso(hv_TLVkgK{h{>5${t#xI~M$J`mrBmDJfj7fa zKHpDcWF6iaS7m?f`S{B6PI=egfi$*G)cKr6zq5Wrq?ytWNn3p>BQ`^oDY`!N1elCj zBpR>1tbAh>_4Xc|r1WKnHKHkX_HK%&tirz)qO2-+Uf|t?u|QckZ}q*ICPE^@z2H-r_HQI5|a}X!qrYtBUur zzXZRzyI7<;oFpU0>AzHrpJh-uulo8>d3y1zDFS^SEP=nflOB#~M)pgguBPVtuy%5O zcB#{iG{YT^sj1K$h23?aPzS46p8jgHd*a(bfyIf7>_3_p7T%Mk# zXF9H{{*ZGWv!HDv(`2;wc_+RhOpPqKjapiTyQ^GhB+y}{vncwz-tM=aa{?jDA#jk1 zEm|&Z;`~@a=_gS_!?eJzx9fG)E&9BTUpAvLxQ$fY5FN2bMMlX&t+*d=Z(W4a%e$UQ zU@pP3LvgC8Du|OI`2eTgb~u%fA7Njvg8|*%ra9BnRFvk@`ZZBEd`(#zp}*~VyE@f? zP{H0^2=4-+(j`V9(}SxretOQ+r&mlxdsgY%Zd5X3f_$9dkPV**G^P<9Pok>Xc>1)V z{Yq^$!h$uo{w&P6AGfBzcH4jXXY1*&|8x7#r?fNUinNk($*AQ6^%ZTkB**?v&d)TL5Z+H%AUEA;n5kXdT^dW7 z5Uv9wE317WdYsNU0|Blg6L1rd<)JJxpAI)NvUS&{cwA2ytqf zn+U=?Dwe&zg9Jg10#+9bIG>#t5+w!gX%AhVo;IIH4(KSPJ4`)}1o6{X#s*%OBeA)^ zX(I^C>}~2~fTowDW8`IEJVh6ZTxn!2oCY*YBa7+_1Pla{x$qUI`WUe> zO-irbf%v=Z(nT(xL?`04Al&?|KeTi0YR5;5Y&=0;&}j1ERjfkz6oz^>(2E;KD)=g5 z{oje7Y+K8gCRyA$C2XmnDhfs;uhGN=3!mbU21b7u5>+dXJ`Z|vtcKZ@f7h`^`FC|s zI@XvA($+ulIQ*jIsjux>LH+{?z3JWmKQ)m5cJ>Yn^55=GZ|70|dx+;Dpxc#Ob8)B13JHC)+LIlJ#DgYN zK?A<28!s_VMqa|wRH)sq3xWtIN&3f2xD4w;Ml`6RinhC5H#2yYAne^2guNvo?4=Nv zK+((HWHU^sr3{HZtkmthVn)g+XywH;Nm3UkdQ6%P2*P>`b7f@? z#V@vYITJTF(bfg2ia8ar{?0L^#4|!tt*E&O zrP1g1`km1Tt~!;mV9SwGXe-xGMYs!{!SzhM)^TC&9eNXB)HpplM((@FWQn~* z&o3@sU8r0DupVR1rZgZbB(y)_fQ4-R78CMiX$(BKzSCoE(aXJb-$khSykv<^*GZ-% zLU@o6jySPHQ*R>{2FSNE&FgT`5g~y|q(^4v?X?bTnZ^h<0jZH6VmcOU0sF@ZSMmI< z)QdXtgNl?X;Yv_#b~=?NJnW3vO*>(ogtJLMBx1x^qEf`c25$1wZDfOQEAngKjA%&O zS@IcG-lPIrVTj)pD(NdbxngC4`~<;>7l?zOdz<`U`>Y`UK@{*c%72H4W%;kS{}})2 zL7oSf|EzPin6o&|8W!O(sS-alwZVU~JBaY}f-%biWaOV@L{w?yD)WR}ybuBcM%~_= z?=O|wOrjVmL4ma=g}_S_)L4a0mYT}q=3`bt2dZZrw%Q}B--PfnjnwXtZzsV+`@q3A zIaObL(Z(9pf`Qqn>E%Hl*VL%ejNJb$>^1h7k1HCg7^L!G9s#cm&p*3eQ##!;gl1#<^+Ev-xuK%{vs#~*7mTV3x zON6%G%OwQhNt_l}hD|3r?3TR%Y#fy=dDk=w&IEGDIL_{ZT2?e0hGT@|Sk7$jttS~^ z(QIZD*D`HYnBNv!-*T7AHBNJst-r)yHy#$7Av40*vTvqa4Yz+>Rf{=Gy~H2ZzTgXr|AIwc0ng&4uD|-$ zVU77aH{Y9;#~Wq7P+jgp*hJ~nB#A?9AX2Ewn}%Gyo-8a^AEm6m$d-qw5j4vz)z8hd z9w+6i=ATMiFdjr(G#WBagnrG*l*;#=m=lvrj79^7{)`4V4Exx>7CKuThfGb{Ltvi# zG&Qb9%c(Oel??J!+Jq+Da$59h+Jp~Rc-Tal=sZ;pP_a5wtjH^%vYd`-6o`)U3sh;A zcXkK+y@2d~hr8b$9QgZW(CrWWy`BE<;Ba@}Kiutg5BrDRZpY--AF_=Xd%gCHy*-o; zDer&v{(rW>{p#D+);mxC^&j@1ZTNoD{%_p=*Sq#Vzt!Ke7pxMA12xXNiK zhE9%-wJT&z!Bi0lJovwxR`q_tzf0*q{YfMH=Q)gbzJh@~AO`OwrMLO|L#_V5iv34l zH*9PGT44X#+dnMXe|GmD<9|HJ^YHOM{tkBQgdoW}C4oXdL7gj2*bY!CJ8&4<0YXmB z&(d_B_R(LN)oLo$y;#meaCb|egz2yPm;QFnB%3wg^snOi)VXyUWwn_eM0p^2LterN zndIH3q#3(iTlSOI?wm!5hu{^63R{e$Xfo~--ZHK#x*DVdgbb{Gq7vwaknu2KkU5vY zSeyn6J(YLR&`*_ypl(3Qpmo`iOhY6nJsd7{SAVS*ENVx zEZedTdvd8s`BhD&Tk1m=EwyKV{AhXiOH`Q#X_Vw!Yh~XCA;ZbRUh%u>4oJ1!;U`IA z6(ZYwyVtq$>`%B9RJF&bdpljWSPS3-Xy0A zm@hgLId2_}4aF#gi-B{zWw{w*exOOnI?ds!?AIpSA`*UwfI;tMO7%tZIbC z&t!&jO}j-z=v?ILD4a_+->BMx;`tzyNhsRb9}~A_v$rq$>IXb4$p1Pv=-SSI_j-H9 z{Exf6-N*Ca2YDV|{Du6JkSZ2s~C_bHd8VScU>9WT2(5?qMh&gU+Tl!alrH4 ztp7r{t=t#gndRwc?ob>Z8%|k@U8{Du()J}^3s3K!sYL6_Me2!>&M911&6tU@dgXq> zIV$q4=2L$#kTff*D9#wP`?HElIwD3oc6EQzlsc`mRc!vI%qU5QtcspfE0$NmOEblz zZ0Z~n4o(A>r87IP$i*!--M1#IZS>Y^A9p!F^E32fZ| zz~iuzU#bN>kCC=amv?3I%QNd%b`@rwoN8IF^To=zk5B!xg8Z)trMdXOQ2syc_6qX< z&f($yqx}C6&x8AaKK~HQO+aorkv5P+SO<_fYPS`eIkmGQIuth41vX^%ZCUcwlyFW1 zBDo`+2yAw)8w0&QtjJ$rd6%fd-WO#GJToDIxf5YyK?+~;^0ACn7^p)9s^boQ3JG(eSc0!=hC0Ba`~L@r#rRLVhmZOHALjXt`Tq?a!O18QNe6an9n=K%v9=0gG^A4!p_mg$ zOmHJq$7Ia-O#a^a?icDJx74MJ-2Lt^)NPpt)hLS1 z?H8K+%T8@`z3$GQX;A*-j;YU^!o5@IXlMW6E^zN2?&q4@{cf*0++vKwup!+01$^)K z>Jiic?!y8a4iD<5uZ(XJ(8+j3z;$bDgS@xDTR(#(&_isU>ntL1ubZQk?%v%<+}qDl zO80OH692Fa;5&K1_qu<%8;$3iJLv5#LE}41LB5yo>brStpaJ9u`Q~~%JKr@X^GqGk zM++&(ROud5eIW|ShvLU_fT=23UkAfDhpG_y^vO5k38E21I`9yP=Ojq)U2y_wN>vwI zDwfB#i?*)GOr#J%&eSw6LXG^tj{ScH`A>zCS)2cN?{IgoB>(O1_a5cHhj<>`{+qgg zD>Bk%lR0)1LvA@!iE*gD)bWj~+|Kf4kgK&)sf#LoN^+H1`sX|c??O{;^gQ>Xlg1uW z@$Yb`e555b=D62#0XO%o;k`b$1P^T`=xX~E9LOR_9BhfNX?SK8HdX1_EFzlED33yF zWL~DcgH3Ftt=}gi(TC3|@>^Pd_OEX+4>AXQ6^0QY_?AWkc2so%NC%`b%p&20bjFxQ z*$0@!4KsSxFW|H^9d-<-43B5vsIFZE!kJ{{Hw2CFR1RV)xqlI(LTdhY(zpBeX3tY- z$$+)kERgeAPpxhh2+B2mT(m!ELsz-plBN~o2U4W^Y%_&>eI>v1R>A!zvMp=|<|-ea zO4DC$Q5(<<17`TurlKO(xhAtlOy}Clz`Y93QR%R+350%d9~sL-Fcv9-RfdCmX`MI9 zLWW;J)y2yj<}#&G*}%~;@}M_+*;cHYe5U)-y2;mQ zB@^Y%#G9yKO52Ed!y_qzi9f;=!>)(k(lA8e=QW^QBr5JBNeI^$0`>aT8kGW7%?WD5 z{i2LpN|@CbJ<_43%>Z#61#FxmPBo*Z6SdI~jD-Y8`KI2E2t@2l7{Wn-#BAJWp)f;U z&~$csRUzgS=0hFg0b_>Pi2_r78YAadi8ki!QB3;UcVba|O4Kd|sFYQtmia1rNp9+f z{Y-BLa-4iZJajeyF+X97?6;A)rm>2WDa?^eL~amhvwRikQ9Wz`?AJ4co_p9PA+y`Fs{9W;w_{{|o$7@6siBx{CB17zYqSlCz2SYcXp` zE(*p2i1Da1w!jJWU=o4-S%)8mSMPP{qDDJ;?HbA(7KghlSstIHl zNPTY1!ZxJ)MCh8?QZuLJDXhwA2PYNXWNB!ClcWg%R9{$#G{Po&a!qD)yGcx*7sWPU zK~Pa7p^V>|Nl>UHLCZmC$3y37&{#9nDaAz&lp#;?w9gI zZ1Y!JDw8N3VO>GeR$<&kGr$z7x?ts?cRh3gd?%b)tf+u4J>nBso|;XSF{-Ce^HdQN zUmdIJAv{qZp5z#_0x0WPjx7Ukp=?#&(^IKqGllHv7`ZM=W9DhbS9qQAS<838b#3>7 z^zcMCthWfOe6Nc+^95K9qVSA_R^SSqq z$yUg22uE9Tr#cDa5k|dMOZ|K~852%@)N7%wNgS6;ebQmle{<{B43rl>rKO#qt^yg7 z4;RW0_aqEo&;-nn?z?wyEnGl6(@#<^7YRrEO&ho4hPo*xvJ%$%`h>)2G8lxUz|iK6 zQ{v_2WLynRjne&5L8ctQqdKUb<471ol#OHNca5*N7Fxw$U?$Tc|cdOjq* z$;2#(Yn6}BB6-2K%9C|N1>C?Fh$cDRpn$7ZJA^ZGn#0OT%Sd;Va3n`lUas_+Kxwl>9$-4<7wL9^(02`5)}~ ztCxzP5X5|{WUQLvd|4n1Jd~#A@k#Y*tW_WjEIncsB8nW8aJaYAQJ}dD5EZD+&?tkp;?-%cX?;bwpe|(tdGw}Z?AUr+vP-jZ%2~z5J z6%#(fu~15y@L8NjE7U)Px&jg<^X^<$#05e5z#tX7MoGIyTKv4P=3N+)i#Bl_&ku}O z?y#&rCZN!}TBmfx&!XHN_m>^lGGD=n*0r%=Z<&%0e zC1?#sO3#7F0;idmI-7&Wb*^aH%Dz;_gOo(lwP;8!3k-xl=^#YVVOil+?@OV0zHD)2 zh5cWhG_EiH%YL_<|F8R~|2@p};QPM{=1e1zaCHk4T!e-s$-H2+CAOY6)b61d_EnNTiei3o1xzYizF96c0Yox*pnqx4Cq_APk%Dr zRBgx#SKJDJn9wjlB$}cr=E~_u`5R*uhGXGG7LcN5(ag+v)=sVYi>Id0lx25@FW?g& zhZMFJA^(6B(Ng`@1GZ|wvEnHf$N$NeTel4}cN)2b$WjPw{d}Ga^=jP8Ls*hM; z2TNY{f0rLN3+@O7F(d1#EoU_vdlr3`b5bMM7yrYry1Z67cFY}n%)Z%{o_7=@KLs7?S28WifTJB&C zF|JGMlM7fv-~k?@h(&E(x(UhEyH*ukxv$Brm#0H;;9iyzbNv-B0O;X^+hF>QlPTeX z{48G{9V7iym&#L_U9>pKtGRhq3$}T+%_4`w?E$aGQ>cnyta|`e6mmKu{KgXBl1Dw$pm{bQA^R5 zAOvmnzS})KXf2i~d=2oGhBn2kg4pf&0Mi)9aOS)$Q!6*05)4x;$$+(Gl00b!Womdo zU_s6$7Xh`aW$*8)nxeH4Hl~Sc*tHX+xYsm-eO05#%`}!zNR05#{(+pbuS0uzeD3~@H6>x$6cd&n0vj6VyKHmR+kmtemzqg8d5bm={f+mvhBhJDQST`QdW6~+% z>GD^2I457?1!KuPo?O|QeP#x+sxwQdxp?RzD}{SY=eR5;Q2eBv1%ISbfZR5J_b&K$ z^ONGPN3KeL`S#@E<=M->AEAqE^e7++@e>lDEvbW9sp4;MkK`L!=JvL=jkJszsaV}A zQaJc|a25^28RA3^lSKNK2c9w-l(7xsC$>T&Ni~jg7O9KVa6L1Clu zj8hzn4%{z+z~XJ)dRw;M*4QIl%(tfIi9%vjkR&m?c`6g=&`=WQFto$gHKQ66m&vvi z=(IJK;AkX9DJq)XRqgK2Kj3j3LN8?}+GV=&1wB_T$=qB9;5z*r{4r(WWK7NpA5$Sz zO}9dDy-LOCNZ$ko_z)IiGB(C&xJJUlklk3xpF#Ifu0~a`WMfDZ)RkVAU@Uykel1>r z8OHMJo3K+g7qzrakdb~NLe4x$;yCjaMVojqfR%Kqk)KoBEn~8ooV=Dt{*Yp{YYE7m zr$}nmmVZ?WHqvdt8EgQI-9f#D% zr)&}>$K7odFzCiGI+|qTPT{cgIb?w7rgLDVRftjK-;XbztZUIb8 zLz%Iy5-#sykPU`d#SN;Huva-w=h}u0Hbw-!0{#s-6)pJE(c4_vNM{~;4bo&*;ao?% z)U;_!paaG&A=5)Y=6>g5^NLiqg=^PU(hK%|#sj@PW^BUI4dd4#!vQQGab(-qN|T{e z$eZNxmRy<_ck`)FAeo}iBA>*d6h{L(oN$0xCs%5xh_J-o&X?|#yuAKz-Cpbb=3^mn z6mUGx7_jL4x3^P_|8~&Zdp!Srkmteme|=SuNWoE&PL>%py?F?%NHP()fjIYuRmEk> zj40Cv>kfxa;_vjfrwQbe*R-15&B#X>GC?VTwPu60!a%A^8Rq6v3UzwkDcTxZ)vvjR zS{1s&OM{bSJ(;j+SY&k>muU6O?sufdTdSfqMXq(|R0w}mafFezpqnfO*2=Y~?+7<) z$cU1;7J~k3LHIc*1Nxy+GuLG1sY->G%#~vMvaz+bXXn)UKJ)ipK{jMu8ICrKv7aCV zc`=F@_*l2hM~x7bl(?W07O(z_;%W6V*QQIut-Ui{LA>rFz%qHM{H^)|bfy3;<^e_r zE!<%??N;2euFeZ*&E1>~43iHuq?pgFI3~)c1r$r*ton?d@j}eExy)_hQZa#Y=di2K zz%~|w-Ojs^rXX4rQ;ZdmwW-!tG0vKO3%Jr&QEswvL+hG&y>hdmEI0RtyH!tm^U267 z+;eBa_6?|?5i_tkxX(Emz>V_iSP)e%E_cPdxs0sldnugTTY-@vQdvQ#h5zDLpFM~L zb6#9!RnwXO^5v>Fk57Z=j{4sibBcrh{G;Fn`rqN#CtNB69io0ojrjPs4`6^Jll*VW3>*nwz8Lh1J722v+`7rlKG$DR6 z;gw!=MS!g0#g-Cl zhFozl34FT%L&aIgx{VM9(`6*D)eKQQc(I2MaE!@C)e*1Gn^<859^(0o_P^QXf4x}0;V+l} z=kuHWV37Cf`2{`PlbNrG%;k)HP7@wp(a-Di*qRpZdF!(@t!v|74D%`)7Bu>G+n8Vojc0pR`%+~a zceO4-qhGjfsj(5GUUa)p6UO~4uRB{V)Vli0{kf{e=&x=zo0|wzZg^jQT&q5JwEx6R zBtuT-8-W)1fAtPa_Md~j$NPT|^L#%0&$%L<`!E7oRQbhNf-;!u%|Th2yV-=SzV62` zRE^-JtV324cMJnl(Nd*}DAg|0^0xZ5xK+vR&0CQfIu`U{=}L3Fi>iC8G^}{}&E_*& zQ_96EF`DXSlgJQbw` z!dzN6VExSzosK${0IJBH@x#P$O2}qcLQ? z>OtcX-3#}~ioQ+{L>F@qkd@0za}M1idyT~b74oxzvtn0m;;mL|j9@uns@CS-)D zw_ztz!sIhMp9kbXEsVpha-wK0tW~Z+{-_+9F%8z4+iT_OCVpT=V{x??SlLN1pu#pj zE`_Spgt!<=J&{wU+zxCOgajtUzsZeCOWX+Jr;_pje@y~qru80@^gx_VPs&iGW_ z#vL^?)#w*#a#A~6{W0q8wHnVvrPy%?`mb!yGIrTJk74Th?Q3fZnX9Gt zLbpctk{#$Qoh$AgMQG=XA9PUHy}e?s9BQA%#p;WG?x_FU+f=O;V1fR>ceq>B{}1+e zAMgJ?$n*L9zqC3m%YdTaCuLn<$?i}d7WOHN2Hmb?E?Y&B&ov3a;gwsb>!;2PT10Ah zyIM@H>8-ck-g;?oO+>yrNl)dGuEb5%5Xz~e3)6sNYqK|_tTu97jffIObgoPTwMoxt zl11tf%yzFXh)P;y~$^&(FV+oGQ3)QFVIbl$u9t9|j9YRBRh zQxq&*lH*z1E}Kci)}N1y=3y62LDeX=j@tR*nWAZ;d&B!OL$VxU3>wJ5TV_$_*G|!9 zxuSS$t+=|&l}e;G1G8*YuA56E>}JYO%Ur^=-jq+fr=@f5%_&ZAjLPMpx3XN3AxTar zoRcUsD!OjbaD$^DVeyzD)W0%rq7WT^a`x0F#q`;nsmujQdvQ8+^^f|?Q}6cdg@8WGR&o?ZTS zDYqwQ1DygzqBp0PD4<+8-jF68_*a4Nc>RCz4*Y9gjD{WgkNGL4Q73DtkNxXO3|fnD zo_gXYcAk2DeC<5-l5y-j{lCrzdV@J-6M@d2Jr|A_bM`y&6UU(WDts0Y z*)|;y(SU^k;SL=SJ727Yq3Qa+cz*Kim*?I%SfP&v_rDIhJBNk!-`zPpd|dwz@obh zV7VT0Hi;!mQACA8Hl#l4ljMewC$3~TdH?>sl!={y#3TwxGoiU)<)1HwBvp7J{W%)CTPO=>)?tljeh=3f);QP%0J{#Ey~ zOX$~UkzgTlUe(&$*^iaD=1^PKUqX*cp=qOYv7iuHdfF zdE^J-4JKizoEhOr+SD4)lx&0CpGX)o^p^23a1{_Q7`v9ZO&FR8g4l#39H)*#LWI>(7%u!Er3I)~@v%FCf#7 zBJ_a7A)Ae*Fe-&()ja%U0|e{oRJBhdoHmmE@rWcupq{t0 z`>oSf4eWZ~dEIYS9jS*B*#`W)jEPTih-ef~5+wW)8DsdefzHNwNG=#lGMY6@L6TQ8 zi^&lZpmsS31w{Cqg6?&Mp20dnrU5Jn)AzWgR8rZf)U!h7OWn&sh!r&O+)hgcXoIm?5m1KTj*BXI&szJvj+)@Ju|B>QV}a#Mu)tswYpj)v1-#6J+tT z!Aq8$b0SETJW=qd^L~*5ggM7E2O*jeWrdgzsZpZC84?EQNHrp2C}9f*)!p6AT@Li5%68P zuFfUb7YT`clC3O#5WEmi=JZsRmqM>;%d?Rn@#r`rVH^+1X^4ebM4NvfF`*1D8Gs3T z!@|j!pi9*df|j0i#qy0RQAd#S; z62n@A*f+L_KqG`T;M+*BF+m9(6CoS8!4WLVp{%VWJFXlOVkQLb(~u@J)h)<=BSN_- zVnd1+W_C9+PTtYDg7@uqyIOqJSXo_mMJU9566zfa$8qzjY6@S)_>T!mW#DW!K7K^$ z#R_BY{e=1ldnEGHE1S2sPHxaa9A+c-JWrb$Ni;o5FE1Ol;{4?D^6jgOXXyu|Y>Pi~ zHa@amB8_NpI2+`@%t4o#9W`QYC@w`osJ>xXJe$aOc}-?>d%U(R(@i_n_pLI=#@k!K zpR-{fTL4-MD=NSE7p*4r#y-H*3+&~qtLK+q@*&CTWOq0q?sgych`8HZ;gGm#%-cnd zfpxfuH-y-S?Y0%|k#yI_$7@7Vna`IkJ43U6J>-moACd#D|>oGKLbRzg9~ zMnP{qDCpe-3VKBpXr_T)$3u<-DX9Ppxnij2b-k|R{Q4?FFWHog`-G$3?l#)#?sT`6 z3O(I1rfhVB1p*aBsz(DFQ86Mxrs5B2GMe~=SPL-e!M;Dj(U1sbuu--Q`8~4~=rslkLc%0dU_53VCrXG7=wLu# zS_T9s6E2$y0s?2RYN)$Pf`V4$I@BUB*fi3v^av151PKRHslq(aaz%===dWIPnk&B@ z;UtCqhH!$YHt7(3>|4ace>hGXopUk;Yr1|Z6h5f12Rso3z0XS|@6nd#8i0rX4E0$u zQk8aKe7gquRzq@Xm9Y-SpKo)g+@`XwAQpwQmWO`*iEuLC-f8Tdk_K>0^`P3o)?1+V z`Z$tA6`(N#9A(Nb?z4$nR~B6F)xo*vw9#vc1tkV)v+Da2Ow(O22i5@p(b>?Y+W=f! zllcN@WQSLIYLajW6f5=D2-{Bqv9R4gg<}&m!UFZB@XmNZBthda9^pPos1GX&B-{}j zvmuFyocS9(Q*yA}0Y)UX6-9L*3FVnRgoE|rX~*O4Yv;gbk+ z$|Qr3JX_C{HoDlg14e}G-momQ9&t326PbaLjRN$9!eX2*m>CPo!o8b2fkROm}{p?*2BWLp8yjjr7mCX3di%mvTS)m`YVMmoAZ8;L?vpwBuCrtmj+xZ^0HSK)qSRR^x_E|jptK7!+&<~v9 zz-MFSV3ZI(My`41su_ZbHSfT!Q_gvZamnP7nDhk=DCQIb4(aX!G>HQ35`Pk7e?$x* z+sNEr+VQ$*OBQhTC%5%iM^fJy&w!96{{|`o20=dfBnAfJvvC|!rG%T!M>pX)|IiJv zeu9B#$ibT3QkOzFaU4MEQKxfrbK_wE(PMnr2~`o%d2x37{N*LMsDAG_ucMF%k!`pA znK>GfJU_%YNV1|KCu)nJ5xU_tfm3V22FVTP#0jWKIPFi89C{2)R3N*6g@R?dUb(LU%h;Gc6IjZZ1@x!wDV|G-Ol4BRIq-d`yMfZE+Mh pAsy31^VHICyf5~+`tf;u9-qhO@mbUJ{{jF2|NniZhx-8H5&$fJX{7)F From 43d86528c1abe289e03a2fd7f099ff526be4f13e Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Tue, 9 Jul 2024 13:38:33 -0700 Subject: [PATCH 03/89] style: initial commit --- litellm/tests/test_anthropic_completion.py | 4203 ++++++++++++++++++++ 1 file changed, 4203 insertions(+) create mode 100644 litellm/tests/test_anthropic_completion.py diff --git a/litellm/tests/test_anthropic_completion.py b/litellm/tests/test_anthropic_completion.py new file mode 100644 index 00000000000..674d0907623 --- /dev/null +++ b/litellm/tests/test_anthropic_completion.py @@ -0,0 +1,4203 @@ +# What is this? +## Unit tests for Anthropic Adapter + +# import asyncio +# import os +# import sys +# import traceback + +# from dotenv import load_dotenv + +# load_dotenv() +# import io +# import os + +# sys.path.insert( +# 0, os.path.abspath("../..") +# ) # Adds the parent directory to the system path +# from unittest.mock import MagicMock, patch + +# import pytest + +# import litellm +# from litellm import ( +# RateLimitError, +# TextCompletionResponse, +# atext_completion, +# completion, +# completion_cost, +# embedding, +# text_completion, +# ) + +# litellm.num_retries = 3 + + +# token_prompt = [ +# [ +# 32, +# 2043, +# 32, +# 329, +# 4585, +# 262, +# 1644, +# 14, +# 34, +# 3705, +# 319, +# 616, +# 47551, +# 30, +# 930, +# 19219, +# 284, +# 1949, +# 284, +# 787, +# 428, +# 355, +# 1790, +# 355, +# 1744, +# 981, +# 1390, +# 3307, +# 2622, +# 13, +# 220, +# 198, +# 198, +# 40, +# 423, +# 587, +# 351, +# 616, +# 41668, +# 32682, +# 329, +# 718, +# 812, +# 13, +# 376, +# 666, +# 32682, +# 468, +# 281, +# 4697, +# 6621, +# 11, +# 356, +# 1183, +# 869, +# 607, +# 25737, +# 11, +# 508, +# 318, +# 2579, +# 290, +# 468, +# 257, +# 642, +# 614, +# 1468, +# 1200, +# 13, +# 314, +# 373, +# 612, +# 262, +# 1110, +# 25737, +# 373, +# 287, +# 4827, +# 290, +# 14801, +# 373, +# 4642, +# 11, +# 673, +# 318, +# 616, +# 41803, +# 13, +# 2399, +# 2104, +# 1641, +# 468, +# 6412, +# 284, +# 502, +# 355, +# 465, +# 38074, +# 494, +# 1201, +# 1110, +# 352, +# 13, +# 314, +# 716, +# 407, +# 2910, +# 475, +# 356, +# 389, +# 1641, +# 11, +# 673, +# 3848, +# 502, +# 38074, +# 494, +# 290, +# 356, +# 423, +# 3993, +# 13801, +# 11, +# 26626, +# 11864, +# 11, +# 3503, +# 13, +# 220, +# 198, +# 198, +# 17, +# 812, +# 2084, +# 25737, +# 373, +# 287, +# 14321, +# 422, +# 2563, +# 13230, +# 13, +# 21051, +# 11, +# 2356, +# 25542, +# 11, +# 290, +# 47482, +# 897, +# 547, +# 607, +# 1517, +# 13, +# 1375, +# 550, +# 257, +# 5110, +# 14608, +# 290, +# 262, +# 1641, +# 7723, +# 1637, +# 284, +# 3758, +# 607, +# 284, +# 14321, +# 290, +# 477, +# 8389, +# 257, +# 7269, +# 284, +# 1011, +# 1337, +# 286, +# 14801, +# 13, +# 383, +# 5156, +# 338, +# 9955, +# 11, +# 25737, +# 338, +# 13850, +# 11, +# 468, +# 257, +# 47973, +# 14, +# 9979, +# 2762, +# 1693, +# 290, +# 373, +# 503, +# 286, +# 3240, +# 329, +# 362, +# 1933, +# 523, +# 339, +# 2492, +# 470, +# 612, +# 329, +# 477, +# 286, +# 428, +# 13, +# 220, +# 198, +# 198, +# 3347, +# 10667, +# 5223, +# 503, +# 706, +# 513, +# 1528, +# 11, +# 23630, +# 673, +# 373, +# 366, +# 38125, +# 290, +# 655, +# 2622, +# 257, +# 3338, +# 8399, +# 1911, +# 314, +# 2298, +# 607, +# 510, +# 11, +# 1011, +# 607, +# 284, +# 607, +# 2156, +# 11, +# 290, +# 673, +# 3393, +# 2925, +# 284, +# 7523, +# 20349, +# 290, +# 4144, +# 257, +# 6099, +# 13, +# 314, +# 836, +# 470, +# 892, +# 20349, +# 318, +# 257, +# 2563, +# 290, +# 716, +# 845, +# 386, +# 12, +# 66, +# 1236, +# 571, +# 292, +# 3584, +# 314, +# 836, +# 470, +# 7523, +# 11, +# 475, +# 326, +# 373, +# 407, +# 5035, +# 6402, +# 314, +# 655, +# 6497, +# 607, +# 510, +# 422, +# 14321, +# 13, +# 220, +# 198, +# 198, +# 32, +# 1285, +# 1568, +# 673, +# 373, +# 6294, +# 329, +# 3013, +# 24707, +# 287, +# 262, +# 12436, +# 1539, +# 819, +# 5722, +# 329, +# 852, +# 604, +# 1933, +# 2739, +# 11, +# 39398, +# 607, +# 1097, +# 5059, +# 981, +# 1029, +# 290, +# 318, +# 852, +# 16334, +# 329, +# 720, +# 1120, +# 74, +# 422, +# 15228, +# 278, +# 656, +# 257, +# 2156, +# 11, +# 290, +# 373, +# 12165, +# 503, +# 286, +# 376, +# 666, +# 32682, +# 338, +# 584, +# 6621, +# 338, +# 2156, +# 329, +# 32012, +# 262, +# 14595, +# 373, +# 30601, +# 510, +# 290, +# 2491, +# 357, +# 7091, +# 373, +# 1029, +# 8, +# 290, +# 262, +# 2104, +# 34624, +# 373, +# 46432, +# 1268, +# 1961, +# 422, +# 1660, +# 2465, +# 780, +# 8168, +# 2073, +# 1625, +# 1363, +# 329, +# 807, +# 2250, +# 13, +# 720, +# 1238, +# 11, +# 830, +# 286, +# 2465, +# 290, +# 5875, +# 5770, +# 511, +# 2156, +# 5096, +# 5017, +# 340, +# 13, +# 220, +# 198, +# 198, +# 2504, +# 373, +# 477, +# 938, +# 614, +# 13, +# 1119, +# 1053, +# 587, +# 287, +# 511, +# 649, +# 2156, +# 319, +# 511, +# 898, +# 329, +# 546, +# 718, +# 1933, +# 13, +# 554, +# 3389, +# 673, +# 1444, +# 34020, +# 290, +# 531, +# 511, +# 8744, +# 373, +# 4423, +# 572, +# 780, +# 673, +# 1422, +# 470, +# 423, +# 262, +# 1637, +# 780, +# 41646, +# 338, +# 37751, +# 1392, +# 32621, +# 510, +# 290, +# 1422, +# 470, +# 467, +# 832, +# 13, +# 679, +# 3432, +# 511, +# 2739, +# 8744, +# 9024, +# 492, +# 257, +# 2472, +# 286, +# 720, +# 4059, +# 13, +# 314, +# 1807, +# 340, +# 373, +# 13678, +# 306, +# 5789, +# 475, +# 4030, +# 616, +# 5422, +# 4423, +# 13, +# 1439, +# 468, +# 587, +# 5897, +# 1201, +# 13, +# 220, +# 198, +# 198, +# 7571, +# 2745, +# 2084, +# 11, +# 673, +# 1965, +# 502, +# 284, +# 8804, +# 617, +# 1637, +# 284, +# 651, +# 38464, +# 329, +# 399, +# 8535, +# 13, +# 3226, +# 1781, +# 314, +# 1101, +# 407, +# 1016, +# 284, +# 1309, +# 616, +# 41803, +# 393, +# 6621, +# 467, +# 14720, +# 11, +# 645, +# 2300, +# 644, +# 318, +# 1016, +# 319, +# 4306, +# 11, +# 523, +# 314, +# 910, +# 314, +# 1183, +# 307, +# 625, +# 379, +# 642, +# 13, +# 314, +# 1392, +# 572, +# 670, +# 1903, +# 290, +# 651, +# 612, +# 379, +# 362, +# 25, +# 2231, +# 13, +# 314, +# 1282, +# 287, +# 1262, +# 616, +# 13952, +# 1994, +# 11, +# 2513, +# 287, +# 11, +# 766, +# 399, +# 8535, +# 2712, +# 351, +# 36062, +# 287, +# 262, +# 5228, +# 11, +# 25737, +# 3804, +# 503, +# 319, +# 262, +# 18507, +# 11, +# 290, +# 16914, +# 319, +# 262, +# 6891, +# 3084, +# 13, +# 8989, +# 2406, +# 422, +# 257, +# 1641, +# 47655, +# 351, +# 13230, +# 11, +# 314, +# 760, +# 644, +# 16914, +# 3073, +# 588, +# 13, +# 314, +# 836, +# 470, +# 760, +# 703, +# 881, +# 340, +# 373, +# 11, +# 475, +# 314, +# 714, +# 423, +# 23529, +# 276, +# 340, +# 510, +# 290, +# 5901, +# 616, +# 18057, +# 351, +# 340, +# 13, +# 314, +# 6810, +# 19772, +# 2024, +# 8347, +# 287, +# 262, +# 2166, +# 2119, +# 290, +# 399, +# 8535, +# 373, +# 287, +# 3294, +# 11685, +# 286, +# 8242, +# 290, +# 607, +# 7374, +# 15224, +# 13, +# 383, +# 4894, +# 373, +# 572, +# 13, +# 383, +# 2156, +# 373, +# 3863, +# 2319, +# 37, +# 532, +# 340, +# 373, +# 1542, +# 2354, +# 13, +# 220, +# 198, +# 198, +# 40, +# 1718, +# 399, +# 8535, +# 284, +# 616, +# 1097, +# 11, +# 290, +# 1444, +# 16679, +# 329, +# 281, +# 22536, +# 355, +# 314, +# 373, +# 12008, +# 25737, +# 373, +# 14904, +# 2752, +# 13, +# 220, +# 314, +# 1422, +# 470, +# 765, +# 284, +# 10436, +# 290, +# 22601, +# 503, +# 399, +# 8535, +# 523, +# 314, +# 9658, +# 287, +# 262, +# 1097, +# 290, +# 1309, +# 607, +# 711, +# 319, +# 616, +# 3072, +# 1566, +# 262, +# 22536, +# 5284, +# 13, +# 3226, +# 1781, +# 1644, +# 290, +# 32084, +# 3751, +# 510, +# 355, +# 880, +# 13, +# 314, +# 4893, +# 262, +# 3074, +# 290, +# 780, +# 399, +# 8535, +# 338, +# 9955, +# 318, +# 503, +# 286, +# 3240, +# 1762, +# 11, +# 34020, +# 14, +# 44, +# 4146, +# 547, +# 1444, +# 13, +# 1649, +# 484, +# 5284, +# 484, +# 547, +# 5897, +# 290, +# 4692, +# 11, +# 1422, +# 470, +# 1107, +# 1561, +# 11, +# 1718, +# 399, +# 8535, +# 11, +# 290, +# 1297, +# 502, +# 284, +# 467, +# 1363, +# 13, +# 220, +# 198, +# 198, +# 2025, +# 1711, +# 1568, +# 314, +# 651, +# 1363, +# 290, +# 41668, +# 32682, +# 7893, +# 502, +# 644, +# 314, +# 1053, +# 1760, +# 13, +# 314, +# 4893, +# 2279, +# 284, +# 683, +# 290, +# 477, +# 339, +# 550, +# 373, +# 8993, +# 329, +# 502, +# 13, +# 18626, +# 262, +# 2104, +# 1641, +# 1541, +# 2993, +# 290, +# 547, +# 28674, +# 379, +# 502, +# 329, +# 644, +# 314, +# 550, +# 1760, +# 13, +# 18626, +# 314, +# 373, +# 366, +# 448, +# 286, +# 1627, +# 290, +# 8531, +# 1, +# 780, +# 314, +# 1444, +# 16679, +# 878, +# 4379, +# 611, +# 673, +# 373, +# 1682, +# 31245, +# 6, +# 278, +# 780, +# 340, +# 2900, +# 503, +# 673, +# 373, +# 655, +# 47583, +# 503, +# 422, +# 262, +# 16914, +# 13, +# 775, +# 8350, +# 329, +# 2250, +# 290, +# 314, +# 1364, +# 290, +# 3377, +# 262, +# 1755, +# 379, +# 616, +# 1266, +# 1545, +# 338, +# 2156, +# 290, +# 16896, +# 477, +# 1755, +# 13, +# 314, +# 3521, +# 470, +# 5412, +# 340, +# 477, +# 523, +# 314, +# 2900, +# 616, +# 3072, +# 572, +# 290, +# 3088, +# 284, +# 8960, +# 290, +# 655, +# 9480, +# 866, +# 13, +# 2011, +# 1266, +# 1545, +# 373, +# 510, +# 477, +# 1755, +# 351, +# 502, +# 11, +# 5149, +# 502, +# 314, +# 750, +# 2147, +# 2642, +# 11, +# 290, +# 314, +# 1101, +# 8788, +# 13, +# 220, +# 198, +# 198, +# 40, +# 1210, +# 616, +# 3072, +# 319, +# 290, +# 314, +# 550, +# 6135, +# 13399, +# 14, +# 37348, +# 1095, +# 13, +# 31515, +# 11, +# 34020, +# 11, +# 47551, +# 11, +# 41668, +# 32682, +# 11, +# 290, +# 511, +# 7083, +# 1641, +# 1866, +# 24630, +# 502, +# 13, +# 1119, +# 389, +# 2282, +# 314, +# 20484, +# 607, +# 1204, +# 11, +# 20484, +# 399, +# 8535, +# 338, +# 1204, +# 11, +# 925, +# 2279, +# 517, +# 8253, +# 621, +# 340, +# 2622, +# 284, +# 307, +# 11, +# 925, +# 340, +# 1171, +# 618, +# 340, +# 373, +# 257, +# 366, +# 17989, +# 14669, +# 1600, +# 290, +# 20484, +# 25737, +# 338, +# 8395, +# 286, +# 1683, +# 1972, +# 20750, +# 393, +# 1719, +# 10804, +# 286, +# 607, +# 1200, +# 757, +# 11, +# 4844, +# 286, +# 606, +# 1683, +# 765, +# 284, +# 766, +# 502, +# 757, +# 290, +# 314, +# 481, +# 1239, +# 766, +# 616, +# 41803, +# 757, +# 11, +# 290, +# 484, +# 765, +# 502, +# 284, +# 1414, +# 329, +# 25737, +# 338, +# 7356, +# 6314, +# 290, +# 20889, +# 502, +# 329, +# 262, +# 32084, +# 1339, +# 290, +# 7016, +# 12616, +# 13, +# 198, +# 198, +# 40, +# 716, +# 635, +# 783, +# 2060, +# 13, +# 1406, +# 319, +# 1353, +# 286, +# 6078, +# 616, +# 1266, +# 1545, +# 286, +# 838, +# 812, +# 357, +# 69, +# 666, +# 32682, +# 828, +# 314, +# 481, +# 4425, +# 616, +# 7962, +# 314, +# 550, +# 351, +# 683, +# 11, +# 644, +# 314, +# 3177, +# 616, +# 1641, +# 11, +# 290, +# 616, +# 399, +# 8535, +# 13, +# 198, +# 198, +# 40, +# 4988, +# 1254, +# 12361, +# 13, +# 314, +# 423, +# 12361, +# 9751, +# 284, +# 262, +# 966, +# 810, +# 314, +# 1101, +# 7960, +# 2130, +# 318, +# 1016, +# 284, +# 1282, +# 651, +# 366, +# 260, +# 18674, +# 1, +# 319, +# 502, +# 329, +# 644, +# 314, +# 750, +# 13, +# 314, +# 460, +# 470, +# 4483, +# 13, +# 314, +# 423, +# 2626, +# 767, +# 8059, +# 422, +# 340, +# 13, +# 314, +# 1101, +# 407, +# 11029, +# 329, +# 7510, +# 13, +# 314, +# 423, +# 11668, +# 739, +# 616, +# 2951, +# 13, +# 314, +# 1053, +# 550, +# 807, +# 50082, +# 12, +# 12545, +# 287, +# 734, +# 2745, +# 13, +# 1629, +# 717, +# 314, +# 2936, +# 523, +# 6563, +# 287, +# 616, +# 2551, +# 475, +# 355, +# 262, +# 1528, +# 467, +# 416, +# 314, +# 1101, +# 3612, +# 3863, +# 484, +# 547, +# 826, +# 290, +# 314, +# 815, +# 423, +# 10667, +# 319, +# 607, +# 878, +# 4585, +# 16679, +# 290, +# 852, +# 5306, +# 3019, +# 992, +# 13, +# 314, +# 836, +# 470, +# 1337, +# 546, +# 25737, +# 7471, +# 11, +# 475, +# 314, +# 750, +# 18344, +# 257, +# 642, +# 614, +# 1468, +# 1200, +# 1497, +# 422, +# 607, +# 3397, +# 290, +# 314, +# 1254, +# 12361, +# 546, +# 340, +# 13, +# 314, +# 760, +# 2130, +# 287, +# 262, +# 1641, +# 481, +# 1011, +# 607, +# 287, +# 11, +# 475, +# 340, +# 338, +# 1239, +# 588, +# 852, +# 351, +# 534, +# 3397, +# 13, +# 1375, +# 481, +# 1663, +# 510, +# 20315, +# 278, +# 502, +# 329, +# 340, +# 290, +# 477, +# 314, +# 1053, +# 1683, +# 1760, +# 318, +# 1842, +# 607, +# 355, +# 616, +# 898, +# 13, +# 220, +# 198, +# 198, +# 22367, +# 11, +# 317, +# 2043, +# 32, +# 30, +# 4222, +# 1037, +# 502, +# 13, +# 383, +# 14934, +# 318, +# 6600, +# 502, +# 6776, +# 13, +# 220, +# 198, +# 24361, +# 25, +# 1148, +# 428, +# 2642, +# 30, +# 198, +# 33706, +# 25, +# 645, +# ], +# [ +# 32, +# 2043, +# 32, +# 329, +# 4585, +# 262, +# 1644, +# 14, +# 34, +# 3705, +# 319, +# 616, +# 47551, +# 30, +# 930, +# 19219, +# 284, +# 1949, +# 284, +# 787, +# 428, +# 355, +# 1790, +# 355, +# 1744, +# 981, +# 1390, +# 3307, +# 2622, +# 13, +# 220, +# 198, +# 198, +# 40, +# 423, +# 587, +# 351, +# 616, +# 41668, +# 32682, +# 329, +# 718, +# 812, +# 13, +# 376, +# 666, +# 32682, +# 468, +# 281, +# 4697, +# 6621, +# 11, +# 356, +# 1183, +# 869, +# 607, +# 25737, +# 11, +# 508, +# 318, +# 2579, +# 290, +# 468, +# 257, +# 642, +# 614, +# 1468, +# 1200, +# 13, +# 314, +# 373, +# 612, +# 262, +# 1110, +# 25737, +# 373, +# 287, +# 4827, +# 290, +# 14801, +# 373, +# 4642, +# 11, +# 673, +# 318, +# 616, +# 41803, +# 13, +# 2399, +# 2104, +# 1641, +# 468, +# 6412, +# 284, +# 502, +# 355, +# 465, +# 38074, +# 494, +# 1201, +# 1110, +# 352, +# 13, +# 314, +# 716, +# 407, +# 2910, +# 475, +# 356, +# 389, +# 1641, +# 11, +# 673, +# 3848, +# 502, +# 38074, +# 494, +# 290, +# 356, +# 423, +# 3993, +# 13801, +# 11, +# 26626, +# 11864, +# 11, +# 3503, +# 13, +# 220, +# 198, +# 198, +# 17, +# 812, +# 2084, +# 25737, +# 373, +# 287, +# 14321, +# 422, +# 2563, +# 13230, +# 13, +# 21051, +# 11, +# 2356, +# 25542, +# 11, +# 290, +# 47482, +# 897, +# 547, +# 607, +# 1517, +# 13, +# 1375, +# 550, +# 257, +# 5110, +# 14608, +# 290, +# 262, +# 1641, +# 7723, +# 1637, +# 284, +# 3758, +# 607, +# 284, +# 14321, +# 290, +# 477, +# 8389, +# 257, +# 7269, +# 284, +# 1011, +# 1337, +# 286, +# 14801, +# 13, +# 383, +# 5156, +# 338, +# 9955, +# 11, +# 25737, +# 338, +# 13850, +# 11, +# 468, +# 257, +# 47973, +# 14, +# 9979, +# 2762, +# 1693, +# 290, +# 373, +# 503, +# 286, +# 3240, +# 329, +# 362, +# 1933, +# 523, +# 339, +# 2492, +# 470, +# 612, +# 329, +# 477, +# 286, +# 428, +# 13, +# 220, +# 198, +# 198, +# 3347, +# 10667, +# 5223, +# 503, +# 706, +# 513, +# 1528, +# 11, +# 23630, +# 673, +# 373, +# 366, +# 38125, +# 290, +# 655, +# 2622, +# 257, +# 3338, +# 8399, +# 1911, +# 314, +# 2298, +# 607, +# 510, +# 11, +# 1011, +# 607, +# 284, +# 607, +# 2156, +# 11, +# 290, +# 673, +# 3393, +# 2925, +# 284, +# 7523, +# 20349, +# 290, +# 4144, +# 257, +# 6099, +# 13, +# 314, +# 836, +# 470, +# 892, +# 20349, +# 318, +# 257, +# 2563, +# 290, +# 716, +# 845, +# 386, +# 12, +# 66, +# 1236, +# 571, +# 292, +# 3584, +# 314, +# 836, +# 470, +# 7523, +# 11, +# 475, +# 326, +# 373, +# 407, +# 5035, +# 6402, +# 314, +# 655, +# 6497, +# 607, +# 510, +# 422, +# 14321, +# 13, +# 220, +# 198, +# 198, +# 32, +# 1285, +# 1568, +# 673, +# 373, +# 6294, +# 329, +# 3013, +# 24707, +# 287, +# 262, +# 12436, +# 1539, +# 819, +# 5722, +# 329, +# 852, +# 604, +# 1933, +# 2739, +# 11, +# 39398, +# 607, +# 1097, +# 5059, +# 981, +# 1029, +# 290, +# 318, +# 852, +# 16334, +# 329, +# 720, +# 1120, +# 74, +# 422, +# 15228, +# 278, +# 656, +# 257, +# 2156, +# 11, +# 290, +# 373, +# 12165, +# 503, +# 286, +# 376, +# 666, +# 32682, +# 338, +# 584, +# 6621, +# 338, +# 2156, +# 329, +# 32012, +# 262, +# 14595, +# 373, +# 30601, +# 510, +# 290, +# 2491, +# 357, +# 7091, +# 373, +# 1029, +# 8, +# 290, +# 262, +# 2104, +# 34624, +# 373, +# 46432, +# 1268, +# 1961, +# 422, +# 1660, +# 2465, +# 780, +# 8168, +# 2073, +# 1625, +# 1363, +# 329, +# 807, +# 2250, +# 13, +# 720, +# 1238, +# 11, +# 830, +# 286, +# 2465, +# 290, +# 5875, +# 5770, +# 511, +# 2156, +# 5096, +# 5017, +# 340, +# 13, +# 220, +# 198, +# 198, +# 2504, +# 373, +# 477, +# 938, +# 614, +# 13, +# 1119, +# 1053, +# 587, +# 287, +# 511, +# 649, +# 2156, +# 319, +# 511, +# 898, +# 329, +# 546, +# 718, +# 1933, +# 13, +# 554, +# 3389, +# 673, +# 1444, +# 34020, +# 290, +# 531, +# 511, +# 8744, +# 373, +# 4423, +# 572, +# 780, +# 673, +# 1422, +# 470, +# 423, +# 262, +# 1637, +# 780, +# 41646, +# 338, +# 37751, +# 1392, +# 32621, +# 510, +# 290, +# 1422, +# 470, +# 467, +# 832, +# 13, +# 679, +# 3432, +# 511, +# 2739, +# 8744, +# 9024, +# 492, +# 257, +# 2472, +# 286, +# 720, +# 4059, +# 13, +# 314, +# 1807, +# 340, +# 373, +# 13678, +# 306, +# 5789, +# 475, +# 4030, +# 616, +# 5422, +# 4423, +# 13, +# 1439, +# 468, +# 587, +# 5897, +# 1201, +# 13, +# 220, +# 198, +# 198, +# 7571, +# 2745, +# 2084, +# 11, +# 673, +# 1965, +# 502, +# 284, +# 8804, +# 617, +# 1637, +# 284, +# 651, +# 38464, +# 329, +# 399, +# 8535, +# 13, +# 3226, +# 1781, +# 314, +# 1101, +# 407, +# 1016, +# 284, +# 1309, +# 616, +# 41803, +# 393, +# 6621, +# 467, +# 14720, +# 11, +# 645, +# 2300, +# 644, +# 318, +# 1016, +# 319, +# 4306, +# 11, +# 523, +# 314, +# 910, +# 314, +# 1183, +# 307, +# 625, +# 379, +# 642, +# 13, +# 314, +# 1392, +# 572, +# 670, +# 1903, +# 290, +# 651, +# 612, +# 379, +# 362, +# 25, +# 2231, +# 13, +# 314, +# 1282, +# 287, +# 1262, +# 616, +# 13952, +# 1994, +# 11, +# 2513, +# 287, +# 11, +# 766, +# 399, +# 8535, +# 2712, +# 351, +# 36062, +# 287, +# 262, +# 5228, +# 11, +# 25737, +# 3804, +# 503, +# 319, +# 262, +# 18507, +# 11, +# 290, +# 16914, +# 319, +# 262, +# 6891, +# 3084, +# 13, +# 8989, +# 2406, +# 422, +# 257, +# 1641, +# 47655, +# 351, +# 13230, +# 11, +# 314, +# 760, +# 644, +# 16914, +# 3073, +# 588, +# 13, +# 314, +# 836, +# 470, +# 760, +# 703, +# 881, +# 340, +# 373, +# 11, +# 475, +# 314, +# 714, +# 423, +# 23529, +# 276, +# 340, +# 510, +# 290, +# 5901, +# 616, +# 18057, +# 351, +# 340, +# 13, +# 314, +# 6810, +# 19772, +# 2024, +# 8347, +# 287, +# 262, +# 2166, +# 2119, +# 290, +# 399, +# 8535, +# 373, +# 287, +# 3294, +# 11685, +# 286, +# 8242, +# 290, +# 607, +# 7374, +# 15224, +# 13, +# 383, +# 4894, +# 373, +# 572, +# 13, +# 383, +# 2156, +# 373, +# 3863, +# 2319, +# 37, +# 532, +# 340, +# 373, +# 1542, +# 2354, +# 13, +# 220, +# 198, +# 198, +# 40, +# 1718, +# 399, +# 8535, +# 284, +# 616, +# 1097, +# 11, +# 290, +# 1444, +# 16679, +# 329, +# 281, +# 22536, +# 355, +# 314, +# 373, +# 12008, +# 25737, +# 373, +# 14904, +# 2752, +# 13, +# 220, +# 314, +# 1422, +# 470, +# 765, +# 284, +# 10436, +# 290, +# 22601, +# 503, +# 399, +# 8535, +# 523, +# 314, +# 9658, +# 287, +# 262, +# 1097, +# 290, +# 1309, +# 607, +# 711, +# 319, +# 616, +# 3072, +# 1566, +# 262, +# 22536, +# 5284, +# 13, +# 3226, +# 1781, +# 1644, +# 290, +# 32084, +# 3751, +# 510, +# 355, +# 880, +# 13, +# 314, +# 4893, +# 262, +# 3074, +# 290, +# 780, +# 399, +# 8535, +# 338, +# 9955, +# 318, +# 503, +# 286, +# 3240, +# 1762, +# 11, +# 34020, +# 14, +# 44, +# 4146, +# 547, +# 1444, +# 13, +# 1649, +# 484, +# 5284, +# 484, +# 547, +# 5897, +# 290, +# 4692, +# 11, +# 1422, +# 470, +# 1107, +# 1561, +# 11, +# 1718, +# 399, +# 8535, +# 11, +# 290, +# 1297, +# 502, +# 284, +# 467, +# 1363, +# 13, +# 220, +# 198, +# 198, +# 2025, +# 1711, +# 1568, +# 314, +# 651, +# 1363, +# 290, +# 41668, +# 32682, +# 7893, +# 502, +# 644, +# 314, +# 1053, +# 1760, +# 13, +# 314, +# 4893, +# 2279, +# 284, +# 683, +# 290, +# 477, +# 339, +# 550, +# 373, +# 8993, +# 329, +# 502, +# 13, +# 18626, +# 262, +# 2104, +# 1641, +# 1541, +# 2993, +# 290, +# 547, +# 28674, +# 379, +# 502, +# 329, +# 644, +# 314, +# 550, +# 1760, +# 13, +# 18626, +# 314, +# 373, +# 366, +# 448, +# 286, +# 1627, +# 290, +# 8531, +# 1, +# 780, +# 314, +# 1444, +# 16679, +# 878, +# 4379, +# 611, +# 673, +# 373, +# 1682, +# 31245, +# 6, +# 278, +# 780, +# 340, +# 2900, +# 503, +# 673, +# 373, +# 655, +# 47583, +# 503, +# 422, +# 262, +# 16914, +# 13, +# 775, +# 8350, +# 329, +# 2250, +# 290, +# 314, +# 1364, +# 290, +# 3377, +# 262, +# 1755, +# 379, +# 616, +# 1266, +# 1545, +# 338, +# 2156, +# 290, +# 16896, +# 477, +# 1755, +# 13, +# 314, +# 3521, +# 470, +# 5412, +# 340, +# 477, +# 523, +# 314, +# 2900, +# 616, +# 3072, +# 572, +# 290, +# 3088, +# 284, +# 8960, +# 290, +# 655, +# 9480, +# 866, +# 13, +# 2011, +# 1266, +# 1545, +# 373, +# 510, +# 477, +# 1755, +# 351, +# 502, +# 11, +# 5149, +# 502, +# 314, +# 750, +# 2147, +# 2642, +# 11, +# 290, +# 314, +# 1101, +# 8788, +# 13, +# 220, +# 198, +# 198, +# 40, +# 1210, +# 616, +# 3072, +# 319, +# 290, +# 314, +# 550, +# 6135, +# 13399, +# 14, +# 37348, +# 1095, +# 13, +# 31515, +# 11, +# 34020, +# 11, +# 47551, +# 11, +# 41668, +# 32682, +# 11, +# 290, +# 511, +# 7083, +# 1641, +# 1866, +# 24630, +# 502, +# 13, +# 1119, +# 389, +# 2282, +# 314, +# 20484, +# 607, +# 1204, +# 11, +# 20484, +# 399, +# 8535, +# 338, +# 1204, +# 11, +# 925, +# 2279, +# 517, +# 8253, +# 621, +# 340, +# 2622, +# 284, +# 307, +# 11, +# 925, +# 340, +# 1171, +# 618, +# 340, +# 373, +# 257, +# 366, +# 17989, +# 14669, +# 1600, +# 290, +# 20484, +# 25737, +# 338, +# 8395, +# 286, +# 1683, +# 1972, +# 20750, +# 393, +# 1719, +# 10804, +# 286, +# 607, +# 1200, +# 757, +# 11, +# 4844, +# 286, +# 606, +# 1683, +# 765, +# 284, +# 766, +# 502, +# 757, +# 290, +# 314, +# 481, +# 1239, +# 766, +# 616, +# 41803, +# 757, +# 11, +# 290, +# 484, +# 765, +# 502, +# 284, +# 1414, +# 329, +# 25737, +# 338, +# 7356, +# 6314, +# 290, +# 20889, +# 502, +# 329, +# 262, +# 32084, +# 1339, +# 290, +# 7016, +# 12616, +# 13, +# 198, +# 198, +# 40, +# 716, +# 635, +# 783, +# 2060, +# 13, +# 1406, +# 319, +# 1353, +# 286, +# 6078, +# 616, +# 1266, +# 1545, +# 286, +# 838, +# 812, +# 357, +# 69, +# 666, +# 32682, +# 828, +# 314, +# 481, +# 4425, +# 616, +# 7962, +# 314, +# 550, +# 351, +# 683, +# 11, +# 644, +# 314, +# 3177, +# 616, +# 1641, +# 11, +# 290, +# 616, +# 399, +# 8535, +# 13, +# 198, +# 198, +# 40, +# 4988, +# 1254, +# 12361, +# 13, +# 314, +# 423, +# 12361, +# 9751, +# 284, +# 262, +# 966, +# 810, +# 314, +# 1101, +# 7960, +# 2130, +# 318, +# 1016, +# 284, +# 1282, +# 651, +# 366, +# 260, +# 18674, +# 1, +# 319, +# 502, +# 329, +# 644, +# 314, +# 750, +# 13, +# 314, +# 460, +# 470, +# 4483, +# 13, +# 314, +# 423, +# 2626, +# 767, +# 8059, +# 422, +# 340, +# 13, +# 314, +# 1101, +# 407, +# 11029, +# 329, +# 7510, +# 13, +# 314, +# 423, +# 11668, +# 739, +# 616, +# 2951, +# 13, +# 314, +# 1053, +# 550, +# 807, +# 50082, +# 12, +# 12545, +# 287, +# 734, +# 2745, +# 13, +# 1629, +# 717, +# 314, +# 2936, +# 523, +# 6563, +# 287, +# 616, +# 2551, +# 475, +# 355, +# 262, +# 1528, +# 467, +# 416, +# 314, +# 1101, +# 3612, +# 3863, +# 484, +# 547, +# 826, +# 290, +# 314, +# 815, +# 423, +# 10667, +# 319, +# 607, +# 878, +# 4585, +# 16679, +# 290, +# 852, +# 5306, +# 3019, +# 992, +# 13, +# 314, +# 836, +# 470, +# 1337, +# 546, +# 25737, +# 7471, +# 11, +# 475, +# 314, +# 750, +# 18344, +# 257, +# 642, +# 614, +# 1468, +# 1200, +# 1497, +# 422, +# 607, +# 3397, +# 290, +# 314, +# 1254, +# 12361, +# 546, +# 340, +# 13, +# 314, +# 760, +# 2130, +# 287, +# 262, +# 1641, +# 481, +# 1011, +# 607, +# 287, +# 11, +# 475, +# 340, +# 338, +# 1239, +# 588, +# 852, +# 351, +# 534, +# 3397, +# 13, +# 1375, +# 481, +# 1663, +# 510, +# 20315, +# 278, +# 502, +# 329, +# 340, +# 290, +# 477, +# 314, +# 1053, +# 1683, +# 1760, +# 318, +# 1842, +# 607, +# 355, +# 616, +# 898, +# 13, +# 220, +# 198, +# 198, +# 22367, +# 11, +# 317, +# 2043, +# 32, +# 30, +# 4222, +# 1037, +# 502, +# 13, +# 383, +# 14934, +# 318, +# 6600, +# 502, +# 6776, +# 13, +# 220, +# 198, +# 24361, +# 25, +# 1148, +# 428, +# 2642, +# 30, +# 198, +# 33706, +# 25, +# 3763, +# ], +# ] + + +# def test_unit_test_text_completion_object(): +# openai_object = { +# "id": "cmpl-99y7B2svVoRWe1xd7UFRmeGjZrFSh", +# "choices": [ +# { +# "finish_reason": "length", +# "index": 0, +# "logprobs": { +# "text_offset": [101], +# "token_logprobs": [-0.00023488728], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00023488728, +# "1": -8.375235, +# "zero": -14.101797, +# "__": -14.554922, +# "00": -14.98461, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 1, +# "logprobs": { +# "text_offset": [116], +# "token_logprobs": [-0.013745008], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.013745008, +# "1": -4.294995, +# "00": -12.287183, +# "2": -12.771558, +# "3": -14.013745, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 2, +# "logprobs": { +# "text_offset": [108], +# "token_logprobs": [-3.655073e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -3.655073e-5, +# "1": -10.656286, +# "__": -11.789099, +# "false": -12.984411, +# "00": -14.039099, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 3, +# "logprobs": { +# "text_offset": [106], +# "token_logprobs": [-0.1345946], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.1345946, +# "1": -2.0720947, +# "2": -12.798657, +# "false": -13.970532, +# "00": -14.27522, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 4, +# "logprobs": { +# "text_offset": [95], +# "token_logprobs": [-0.10491652], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.10491652, +# "1": -2.3236666, +# "2": -7.0111666, +# "3": -7.987729, +# "4": -9.050229, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 5, +# "logprobs": { +# "text_offset": [121], +# "token_logprobs": [-0.00026300468], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00026300468, +# "1": -8.250263, +# "zero": -14.976826, +# " ": -15.461201, +# "000": -15.773701, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 6, +# "logprobs": { +# "text_offset": [146], +# "token_logprobs": [-5.085517e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -5.085517e-5, +# "1": -9.937551, +# "000": -13.929738, +# "__": -14.968801, +# "zero": -15.070363, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 7, +# "logprobs": { +# "text_offset": [100], +# "token_logprobs": [-0.13875218], +# "tokens": ["1"], +# "top_logprobs": [ +# { +# "1": -0.13875218, +# "0": -2.0450022, +# "2": -9.7559395, +# "3": -11.1465645, +# "4": -11.5528145, +# } +# ], +# }, +# "text": "1", +# }, +# { +# "finish_reason": "length", +# "index": 8, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0005573204], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0005573204, +# "1": -7.6099324, +# "3": -10.070869, +# "2": -11.617744, +# " ": -12.859932, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 9, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0018747397], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0018747397, +# "1": -6.29875, +# "3": -11.2675, +# "4": -11.634687, +# "2": -11.822187, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 10, +# "logprobs": { +# "text_offset": [110], +# "token_logprobs": [-0.003476763], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.003476763, +# "1": -5.6909766, +# "__": -10.526915, +# "None": -10.925352, +# "False": -11.88629, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 11, +# "logprobs": { +# "text_offset": [106], +# "token_logprobs": [-0.00032962486], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00032962486, +# "1": -8.03158, +# "__": -13.445642, +# "2": -13.828455, +# "zero": -15.453455, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 12, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-9.984788e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -9.984788e-5, +# "1": -9.21885, +# " ": -14.836038, +# "zero": -16.265724, +# "00": -16.578224, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 13, +# "logprobs": { +# "text_offset": [106], +# "token_logprobs": [-0.0010039895], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0010039895, +# "1": -6.907254, +# "2": -13.743192, +# "false": -15.227567, +# "3": -15.297879, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 14, +# "logprobs": { +# "text_offset": [106], +# "token_logprobs": [-0.0005681643], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0005681643, +# "1": -7.5005684, +# "__": -11.836506, +# "zero": -13.242756, +# "file": -13.445881, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 15, +# "logprobs": { +# "text_offset": [146], +# "token_logprobs": [-3.9769227e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -3.9769227e-5, +# "1": -10.15629, +# "000": -15.078165, +# "00": -15.664103, +# "zero": -16.015665, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 16, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0006509595], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0006509595, +# "1": -7.344401, +# "2": -13.352214, +# " ": -13.852214, +# "3": -14.680339, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 17, +# "logprobs": { +# "text_offset": [103], +# "token_logprobs": [-0.0093299495], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0093299495, +# "1": -4.681205, +# "2": -11.173392, +# "3": -13.439017, +# "00": -14.673392, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 18, +# "logprobs": { +# "text_offset": [130], +# "token_logprobs": [-0.00024382756], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00024382756, +# "1": -8.328369, +# " ": -13.640869, +# "zero": -14.859619, +# "null": -16.51587, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 19, +# "logprobs": { +# "text_offset": [107], +# "token_logprobs": [-0.0006452414], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0006452414, +# "1": -7.36002, +# "00": -12.328771, +# "000": -12.961583, +# "2": -14.211583, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 20, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0012751155], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0012751155, +# "1": -6.67315, +# "__": -11.970025, +# "<|endoftext|>": -14.907525, +# "3": -14.930963, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 21, +# "logprobs": { +# "text_offset": [107], +# "token_logprobs": [-7.1954215e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -7.1954215e-5, +# "1": -9.640697, +# "00": -13.500072, +# "000": -13.523509, +# "__": -13.945384, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 22, +# "logprobs": { +# "text_offset": [108], +# "token_logprobs": [-0.0032367748], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0032367748, +# "1": -5.737612, +# "<|endoftext|>": -13.940737, +# "2": -14.167299, +# "00": -14.292299, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 23, +# "logprobs": { +# "text_offset": [117], +# "token_logprobs": [-0.00018673266], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00018673266, +# "1": -8.593937, +# "zero": -15.179874, +# "null": -15.515812, +# "None": -15.851749, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 24, +# "logprobs": { +# "text_offset": [104], +# "token_logprobs": [-0.0010223285], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0010223285, +# "1": -6.8916473, +# "__": -13.05571, +# "00": -14.071335, +# "zero": -14.235397, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 25, +# "logprobs": { +# "text_offset": [108], +# "token_logprobs": [-0.0038979414], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0038979414, +# "1": -5.550773, +# "2": -13.160148, +# "00": -14.144523, +# "3": -14.41796, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 26, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.00074721366], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00074721366, +# "1": -7.219497, +# "3": -11.430435, +# "2": -13.367935, +# " ": -13.735123, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 27, +# "logprobs": { +# "text_offset": [146], +# "token_logprobs": [-8.566264e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -8.566264e-5, +# "1": -9.375086, +# "000": -15.359461, +# "__": -15.671961, +# "00": -15.679773, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 28, +# "logprobs": { +# "text_offset": [119], +# "token_logprobs": [-0.000274683], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.000274683, +# "1": -8.2034, +# "00": -14.898712, +# "2": -15.633087, +# "__": -16.844025, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 29, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.014869375], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.014869375, +# "1": -4.217994, +# "2": -11.63987, +# "3": -11.944557, +# "5": -12.26487, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 30, +# "logprobs": { +# "text_offset": [110], +# "token_logprobs": [-0.010907865], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.010907865, +# "1": -4.5265326, +# "2": -11.440596, +# "<|endoftext|>": -12.456221, +# "file": -13.049971, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 31, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.00070528337], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00070528337, +# "1": -7.2663302, +# "6": -13.141331, +# "2": -13.797581, +# "3": -13.836643, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 32, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0004983439], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0004983439, +# "1": -7.6098733, +# "3": -14.211436, +# "2": -14.336436, +# " ": -15.117686, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 33, +# "logprobs": { +# "text_offset": [110], +# "token_logprobs": [-3.6908343e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -3.6908343e-5, +# "1": -10.250037, +# "00": -14.2266, +# "__": -14.7266, +# "000": -16.164099, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 34, +# "logprobs": { +# "text_offset": [104], +# "token_logprobs": [-0.003917157], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.003917157, +# "1": -5.550792, +# "2": -11.355479, +# "00": -12.777354, +# "3": -13.652354, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 35, +# "logprobs": { +# "text_offset": [146], +# "token_logprobs": [-5.0139948e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -5.0139948e-5, +# "1": -9.921926, +# "000": -14.851613, +# "00": -15.414113, +# "zero": -15.687551, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 36, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0005143099], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0005143099, +# "1": -7.5786395, +# " ": -14.406764, +# "00": -14.570827, +# "999": -14.633327, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 37, +# "logprobs": { +# "text_offset": [103], +# "token_logprobs": [-0.00013691289], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00013691289, +# "1": -8.968887, +# "__": -12.547012, +# "zero": -13.57045, +# "00": -13.8517, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 38, +# "logprobs": { +# "text_offset": [103], +# "token_logprobs": [-0.00032569113], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00032569113, +# "1": -8.047201, +# "2": -13.570639, +# "zero": -14.023764, +# "false": -14.726889, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 39, +# "logprobs": { +# "text_offset": [113], +# "token_logprobs": [-3.7146747e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -3.7146747e-5, +# "1": -10.203162, +# "zero": -18.437536, +# "2": -20.117224, +# " zero": -20.210974, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 40, +# "logprobs": { +# "text_offset": [110], +# "token_logprobs": [-7.4695905e-5], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -7.4695905e-5, +# "1": -9.515699, +# "00": -14.836012, +# "__": -16.093824, +# "file": -16.468824, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 41, +# "logprobs": { +# "text_offset": [111], +# "token_logprobs": [-0.02289473], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.02289473, +# "1": -3.7885196, +# "2": -12.499457, +# "3": -14.546332, +# "00": -15.66352, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 42, +# "logprobs": { +# "text_offset": [108], +# "token_logprobs": [-0.0011367622], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0011367622, +# "1": -6.782387, +# "2": -13.493324, +# "00": -15.071449, +# "zero": -15.727699, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 43, +# "logprobs": { +# "text_offset": [115], +# "token_logprobs": [-0.0006384541], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0006384541, +# "1": -7.3600135, +# "00": -14.0397005, +# "2": -14.4303255, +# "000": -15.563138, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 44, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0007382771], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0007382771, +# "1": -7.219488, +# "4": -13.516363, +# "2": -13.555426, +# "3": -13.602301, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 45, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0014242834], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0014242834, +# "1": -6.5639243, +# "2": -12.493611, +# "__": -12.712361, +# "3": -12.884236, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 46, +# "logprobs": { +# "text_offset": [111], +# "token_logprobs": [-0.00017088225], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00017088225, +# "1": -8.765796, +# "zero": -12.695483, +# "__": -12.804858, +# "time": -12.882983, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 47, +# "logprobs": { +# "text_offset": [146], +# "token_logprobs": [-0.000107238506], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.000107238506, +# "1": -9.171982, +# "000": -13.648544, +# "__": -14.531357, +# "zero": -14.586044, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 48, +# "logprobs": { +# "text_offset": [106], +# "token_logprobs": [-0.0028172398], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0028172398, +# "1": -5.877817, +# "00": -12.16688, +# "2": -12.487192, +# "000": -14.182505, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 49, +# "logprobs": { +# "text_offset": [104], +# "token_logprobs": [-0.00043460296], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.00043460296, +# "1": -7.7816844, +# "00": -13.570747, +# "2": -13.60981, +# "__": -13.789497, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 50, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0046973573], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0046973573, +# "1": -5.3640723, +# "null": -14.082823, +# " ": -14.707823, +# "2": -14.746885, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 51, +# "logprobs": { +# "text_offset": [100], +# "token_logprobs": [-0.2487161], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.2487161, +# "1": -1.5143411, +# "2": -9.037779, +# "3": -10.100279, +# "4": -10.756529, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 52, +# "logprobs": { +# "text_offset": [108], +# "token_logprobs": [-0.0011751055], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0011751055, +# "1": -6.751175, +# " ": -13.73555, +# "2": -15.258987, +# "3": -15.399612, +# } +# ], +# }, +# "text": "0", +# }, +# { +# "finish_reason": "length", +# "index": 53, +# "logprobs": { +# "text_offset": [143], +# "token_logprobs": [-0.0012339224], +# "tokens": ["0"], +# "top_logprobs": [ +# { +# "0": -0.0012339224, +# "1": -6.719984, +# "6": -11.430922, +# "3": -12.165297, +# "2": -12.696547, +# } +# ], +# }, +# "text": "0", +# }, +# ], +# "created": 1712163061, +# "model": "ft:babbage-002:ai-r-d-zapai:v3-fields-used:84jb9rtr", +# "object": "text_completion", +# "system_fingerprint": None, +# "usage": {"completion_tokens": 54, "prompt_tokens": 1877, "total_tokens": 1931}, +# } + +# text_completion_obj = TextCompletionResponse(**openai_object) + +# ## WRITE UNIT TESTS FOR TEXT_COMPLETION_OBJECT +# assert text_completion_obj.id == "cmpl-99y7B2svVoRWe1xd7UFRmeGjZrFSh" +# assert text_completion_obj.object == "text_completion" +# assert text_completion_obj.created == 1712163061 +# assert ( +# text_completion_obj.model +# == "ft:babbage-002:ai-r-d-zapai:v3-fields-used:84jb9rtr" +# ) +# assert text_completion_obj.system_fingerprint == None +# assert len(text_completion_obj.choices) == len(openai_object["choices"]) + +# # TEST FIRST CHOICE # +# first_text_completion_obj = text_completion_obj.choices[0] +# assert first_text_completion_obj.index == 0 +# assert first_text_completion_obj.logprobs.text_offset == [101] +# assert first_text_completion_obj.logprobs.tokens == ["0"] +# assert first_text_completion_obj.logprobs.token_logprobs == [-0.00023488728] +# assert len(first_text_completion_obj.logprobs.top_logprobs) == len( +# openai_object["choices"][0]["logprobs"]["top_logprobs"] +# ) +# assert first_text_completion_obj.text == "0" +# assert first_text_completion_obj.finish_reason == "length" + +# # TEST SECOND CHOICE # +# second_text_completion_obj = text_completion_obj.choices[1] +# assert second_text_completion_obj.index == 1 +# assert second_text_completion_obj.logprobs.text_offset == [116] +# assert second_text_completion_obj.logprobs.tokens == ["0"] +# assert second_text_completion_obj.logprobs.token_logprobs == [-0.013745008] +# assert len(second_text_completion_obj.logprobs.top_logprobs) == len( +# openai_object["choices"][0]["logprobs"]["top_logprobs"] +# ) +# assert second_text_completion_obj.text == "0" +# assert second_text_completion_obj.finish_reason == "length" + +# # TEST LAST CHOICE # +# last_text_completion_obj = text_completion_obj.choices[-1] +# assert last_text_completion_obj.index == 53 +# assert last_text_completion_obj.logprobs.text_offset == [143] +# assert last_text_completion_obj.logprobs.tokens == ["0"] +# assert last_text_completion_obj.logprobs.token_logprobs == [-0.0012339224] +# assert len(last_text_completion_obj.logprobs.top_logprobs) == len( +# openai_object["choices"][0]["logprobs"]["top_logprobs"] +# ) +# assert last_text_completion_obj.text == "0" +# assert last_text_completion_obj.finish_reason == "length" + +# assert text_completion_obj.usage.completion_tokens == 54 +# assert text_completion_obj.usage.prompt_tokens == 1877 +# assert text_completion_obj.usage.total_tokens == 1931 + + +# def test_completion_openai_prompt(): +# try: +# print("\n text 003 test\n") +# response = text_completion( +# model="gpt-3.5-turbo-instruct", +# prompt=["What's the weather in SF?", "How is Manchester?"], +# ) +# print(response) +# assert len(response.choices) == 2 +# response_str = response["choices"][0]["text"] +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_openai_prompt() + + +# def test_completion_openai_engine_and_model(): +# try: +# print("\n text 003 test\n") +# litellm.set_verbose = True +# response = text_completion( +# model="gpt-3.5-turbo-instruct", +# engine="anything", +# prompt="What's the weather in SF?", +# max_tokens=5, +# ) +# print(response) +# response_str = response["choices"][0]["text"] +# # print(response.choices[0]) +# # print(response.choices[0].text) +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_openai_engine_and_model() + + +# def test_completion_openai_engine(): +# try: +# print("\n text 003 test\n") +# litellm.set_verbose = True +# response = text_completion( +# engine="gpt-3.5-turbo-instruct", +# prompt="What's the weather in SF?", +# max_tokens=5, +# ) +# print(response) +# response_str = response["choices"][0]["text"] +# # print(response.choices[0]) +# # print(response.choices[0].text) +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_openai_engine() + + +# def test_completion_chatgpt_prompt(): +# try: +# print("\n gpt3.5 test\n") +# response = text_completion( +# model="gpt-3.5-turbo", prompt="What's the weather in SF?" +# ) +# print(response) +# response_str = response["choices"][0]["text"] +# print("\n", response.choices) +# print("\n", response.choices[0]) +# # print(response.choices[0].text) +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_chatgpt_prompt() + + +# def test_text_completion_basic(): +# try: +# print("\n test 003 with logprobs \n") +# litellm.set_verbose = False +# response = text_completion( +# model="gpt-3.5-turbo-instruct", +# prompt="good morning", +# max_tokens=10, +# logprobs=10, +# ) +# print(response) +# print(response.choices) +# print(response.choices[0]) +# # print(response.choices[0].text) +# response_str = response["choices"][0]["text"] +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_text_completion_basic() + + +# def test_completion_text_003_prompt_array(): +# try: +# litellm.set_verbose = False +# response = text_completion( +# model="gpt-3.5-turbo-instruct", +# prompt=token_prompt, # token prompt is a 2d list +# ) +# print("\n\n response") + +# print(response) +# # response_str = response["choices"][0]["text"] +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_text_003_prompt_array() + + +# # not including this in our ci cd pipeline, since we don't want to fail tests due to an unstable replit +# # def test_text_completion_with_proxy(): +# # try: +# # litellm.set_verbose=True +# # response = text_completion( +# # model="facebook/opt-125m", +# # prompt='Write a tagline for a traditional bavarian tavern', +# # api_base="https://openai-proxy.berriai.repl.co/v1", +# # custom_llm_provider="openai", +# # temperature=0, +# # max_tokens=10, +# # ) +# # print("\n\n response") + +# # print(response) +# # except Exception as e: +# # pytest.fail(f"Error occurred: {e}") +# # test_text_completion_with_proxy() + + +# ##### hugging face tests +# def test_completion_hf_prompt_array(): +# try: +# litellm.set_verbose = True +# print("\n testing hf mistral\n") +# response = text_completion( +# model="huggingface/mistralai/Mistral-7B-v0.1", +# prompt=token_prompt, # token prompt is a 2d list, +# max_tokens=0, +# temperature=0.0, +# # echo=True, # hugging face inference api is currently raising errors for this, looks like they have a regression on their side +# ) +# print("\n\n response") + +# print(response) +# print(response.choices) +# assert len(response.choices) == 2 +# # response_str = response["choices"][0]["text"] +# except Exception as e: +# print(str(e)) +# if "is currently loading" in str(e): +# return +# if "Service Unavailable" in str(e): +# return +# pytest.fail(f"Error occurred: {e}") + + +# # test_completion_hf_prompt_array() + + +# def test_text_completion_stream(): +# try: +# response = text_completion( +# model="huggingface/mistralai/Mistral-7B-v0.1", +# prompt="good morning", +# stream=True, +# max_tokens=10, +# ) +# for chunk in response: +# print(f"chunk: {chunk}") +# except Exception as e: +# pytest.fail(f"GOT exception for HF In streaming{e}") + + +# # test_text_completion_stream() + +# # async def test_text_completion_async_stream(): +# # try: +# # response = await atext_completion( +# # model="text-completion-openai/gpt-3.5-turbo-instruct", +# # prompt="good morning", +# # stream=True, +# # max_tokens=10, +# # ) +# # async for chunk in response: +# # print(f"chunk: {chunk}") +# # except Exception as e: +# # pytest.fail(f"GOT exception for HF In streaming{e}") + +# # asyncio.run(test_text_completion_async_stream()) + + +# def test_async_text_completion(): +# litellm.set_verbose = True +# print("test_async_text_completion") + +# async def test_get_response(): +# try: +# response = await litellm.atext_completion( +# model="gpt-3.5-turbo-instruct", +# prompt="good morning", +# stream=False, +# max_tokens=10, +# ) +# print(f"response: {response}") +# except litellm.Timeout as e: +# print(e) +# except Exception as e: +# print(e) + +# asyncio.run(test_get_response()) + + +# @pytest.mark.skip(reason="Skip flaky tgai test") +# def test_async_text_completion_together_ai(): +# litellm.set_verbose = True +# print("test_async_text_completion") + +# async def test_get_response(): +# try: +# response = await litellm.atext_completion( +# model="together_ai/mistralai/Mixtral-8x7B-Instruct-v0.1", +# prompt="good morning", +# max_tokens=10, +# ) +# print(f"response: {response}") +# except litellm.Timeout as e: +# print(e) +# except Exception as e: +# pytest.fail("An unexpected error occurred") + +# asyncio.run(test_get_response()) + + +# # test_async_text_completion() + + +# def test_async_text_completion_stream(): +# # tests atext_completion + streaming - assert only one finish reason sent +# litellm.set_verbose = False +# print("test_async_text_completion with stream") + +# async def test_get_response(): +# try: +# response = await litellm.atext_completion( +# model="gpt-3.5-turbo-instruct", +# prompt="good morning", +# stream=True, +# ) +# print(f"response: {response}") + +# num_finish_reason = 0 +# async for chunk in response: +# print(chunk) +# if chunk["choices"][0].get("finish_reason") is not None: +# num_finish_reason += 1 +# print("finish_reason", chunk["choices"][0].get("finish_reason")) + +# assert ( +# num_finish_reason == 1 +# ), f"expected only one finish reason. Got {num_finish_reason}" +# except Exception as e: +# pytest.fail(f"GOT exception for gpt-3.5 instruct In streaming{e}") + +# asyncio.run(test_get_response()) + + +# # test_async_text_completion_stream() + + +# @pytest.mark.asyncio +# async def test_async_text_completion_chat_model_stream(): +# try: +# response = await litellm.atext_completion( +# model="gpt-3.5-turbo", +# prompt="good morning", +# stream=True, +# max_tokens=10, +# ) + +# num_finish_reason = 0 +# chunks = [] +# async for chunk in response: +# print(chunk) +# chunks.append(chunk) +# if chunk["choices"][0].get("finish_reason") is not None: +# num_finish_reason += 1 + +# assert ( +# num_finish_reason == 1 +# ), f"expected only one finish reason. Got {num_finish_reason}" +# response_obj = litellm.stream_chunk_builder(chunks=chunks) +# cost = litellm.completion_cost(completion_response=response_obj) +# assert cost > 0 +# except Exception as e: +# pytest.fail(f"GOT exception for gpt-3.5 In streaming{e}") + + +# # asyncio.run(test_async_text_completion_chat_model_stream()) + + +# @pytest.mark.asyncio +# async def test_completion_codestral_fim_api(): +# try: +# litellm.set_verbose = True +# import logging + +# from litellm._logging import verbose_logger + +# verbose_logger.setLevel(level=logging.DEBUG) +# response = await litellm.atext_completion( +# model="text-completion-codestral/codestral-2405", +# prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():", +# suffix="return True", +# temperature=0, +# top_p=1, +# max_tokens=10, +# min_tokens=10, +# seed=10, +# stop=["return"], +# ) +# # Add any assertions here to check the response +# print(response) + +# assert response.choices[0].text is not None +# assert len(response.choices[0].text) > 0 + +# # cost = litellm.completion_cost(completion_response=response) +# # print("cost to make mistral completion=", cost) +# # assert cost > 0.0 +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# @pytest.mark.asyncio +# async def test_completion_codestral_fim_api_stream(): +# try: +# import logging + +# from litellm._logging import verbose_logger + +# litellm.set_verbose = False + +# # verbose_logger.setLevel(level=logging.DEBUG) +# response = await litellm.atext_completion( +# model="text-completion-codestral/codestral-2405", +# prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():", +# suffix="return True", +# temperature=0, +# top_p=1, +# stream=True, +# seed=10, +# stop=["return"], +# ) + +# full_response = "" +# # Add any assertions here to check the response +# async for chunk in response: +# print(chunk) +# full_response += chunk.get("choices")[0].get("text") or "" + +# print("full_response", full_response) + +# assert len(full_response) > 2 # we at least have a few chars in response :) + +# # cost = litellm.completion_cost(completion_response=response) +# # print("cost to make mistral completion=", cost) +# # assert cost > 0.0 +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") + + +# def mock_post(*args, **kwargs): +# mock_response = MagicMock() +# mock_response.status_code = 200 +# mock_response.headers = {"Content-Type": "application/json"} +# mock_response.model_dump.return_value = { +# "id": "cmpl-7a59383dd4234092b9e5d652a7ab8143", +# "object": "text_completion", +# "created": 1718824735, +# "model": "Sao10K/L3-70B-Euryale-v2.1", +# "choices": [ +# { +# "index": 0, +# "text": ") might be faster than then answering, and the added time it takes for the", +# "logprobs": None, +# "finish_reason": "length", +# "stop_reason": None, +# } +# ], +# "usage": {"prompt_tokens": 2, "total_tokens": 18, "completion_tokens": 16}, +# } +# return mock_response + + +# def test_completion_vllm(): +# """ +# Asserts a text completion call for vllm actually goes to the text completion endpoint +# """ +# from openai import OpenAI + +# client = OpenAI(api_key="my-fake-key") + +# with patch.object(client.completions, "create", side_effect=mock_post) as mock_call: +# response = text_completion( +# model="openai/gemini-1.5-flash", prompt="ping", client=client, hello="world" +# ) +# print(response) + +# assert response.usage.prompt_tokens == 2 + +# mock_call.assert_called_once() + +# assert "hello" in mock_call.call_args.kwargs["extra_body"] From 5d6e172d5ccfecb203ba8f8392700d604a9e3db8 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 00:32:28 -0700 Subject: [PATCH 04/89] feat(anthropic_adapter.py): support for translating anthropic params to openai format --- litellm/__init__.py | 7 +- litellm/adapters/anthropic_adapter.py | 43 + litellm/integrations/custom_logger.py | 24 + litellm/llms/anthropic.py | 223 ++ litellm/main.py | 28 + litellm/proxy/_new_secret_config.yaml | 11 - litellm/tests/test_anthropic_completion.py | 4214 +------------------- litellm/types/adapter.py | 10 + litellm/types/llms/anthropic.py | 86 +- litellm/types/llms/openai.py | 109 +- 10 files changed, 542 insertions(+), 4213 deletions(-) create mode 100644 litellm/adapters/anthropic_adapter.py create mode 100644 litellm/types/adapter.py diff --git a/litellm/__init__.py b/litellm/__init__.py index f0dab5e29ee..ce98febcd8d 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -364,7 +364,7 @@ for key, value in model_cost.items(): elif value.get("litellm_provider") == "mistral": mistral_chat_models.append(key) elif value.get("litellm_provider") == "anthropic": - anthropic_models.append(key) + anthropic_models.append(key) elif value.get("litellm_provider") == "empower": empower_models.append(key) elif value.get("litellm_provider") == "openrouter": @@ -881,3 +881,8 @@ from .assistants.main import * from .batches.main import * from .scheduler import * from .cost_calculator import response_cost_calculator, cost_per_token + +### ADAPTERS ### +from .types.adapter import AdapterItem + +adapters: List[AdapterItem] = [] diff --git a/litellm/adapters/anthropic_adapter.py b/litellm/adapters/anthropic_adapter.py new file mode 100644 index 00000000000..ce75755ca00 --- /dev/null +++ b/litellm/adapters/anthropic_adapter.py @@ -0,0 +1,43 @@ +# What is this? +## Translates OpenAI call to Anthropic `/v1/messages` format +import json +import os +import traceback +import uuid +from typing import Literal, Optional + +import dotenv +import httpx + +import litellm +from litellm import ChatCompletionRequest, verbose_logger +from litellm.integrations.custom_logger import CustomLogger +from litellm.types.llms.anthropic import AnthropicMessagesRequest + + +class AnthropicAdapter(CustomLogger): + def __init__(self) -> None: + super().__init__() + + def translate_completion_input_params( + self, kwargs + ) -> Optional[ChatCompletionRequest]: + """ + - translate params, where needed + - pass rest, as is + """ + request_body = AnthropicMessagesRequest(**kwargs) # type: ignore + + translated_body = litellm.AnthropicConfig().translate_anthropic_to_openai( + anthropic_message_request=request_body + ) + return translated_body + + def translate_completion_output_params(self, response: litellm.ModelResponse): + return super().translate_completion_output_params(response) + + def translate_completion_output_params_streaming(self): + return super().translate_completion_output_params_streaming() + + +anthropic_adapter = AnthropicAdapter() diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py index da9826b9b52..4c3fa3a1378 100644 --- a/litellm/integrations/custom_logger.py +++ b/litellm/integrations/custom_logger.py @@ -8,6 +8,8 @@ import dotenv from litellm.caching import DualCache from litellm.proxy._types import UserAPIKeyAuth +from litellm.types.llms.openai import ChatCompletionRequest +from litellm.types.utils import ModelResponse class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callback#callback-class @@ -55,6 +57,28 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac def pre_call_check(self, deployment: dict) -> Optional[dict]: pass + #### ADAPTERS #### Allow calling 100+ LLMs in custom format - https://github.com/BerriAI/litellm/pulls + + def translate_completion_input_params( + self, kwargs + ) -> Optional[ChatCompletionRequest]: + """ + Translates the input params, from the provider's native format to the litellm.completion() format. + """ + pass + + def translate_completion_output_params(self, response: ModelResponse): + """ + Translates the output params, from the OpenAI format to the custom format. + """ + pass + + def translate_completion_output_params_streaming(self): + """ + Translates the streaming chunk, from the OpenAI format to the custom format. + """ + pass + #### CALL HOOKS - proxy only #### """ Control the modify incoming / outgoung data before calling the model diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index a4521a7031f..02e222b9059 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -20,17 +20,36 @@ from litellm.llms.custom_httpx.http_handler import ( _get_httpx_client, ) from litellm.types.llms.anthropic import ( + AnthopicMessagesAssistantMessageParam, + AnthropicMessagesRequest, + AnthropicMessagesTool, AnthropicMessagesToolChoice, + AnthropicMessagesUserMessageParam, ContentBlockDelta, ContentBlockStart, MessageBlockDelta, MessageStartBlock, ) from litellm.types.llms.openai import ( + AllMessageValues, + ChatCompletionAssistantMessage, + ChatCompletionAssistantToolCall, + ChatCompletionImageObject, + ChatCompletionImageUrlObject, + ChatCompletionRequest, ChatCompletionResponseMessage, + ChatCompletionSystemMessage, + ChatCompletionTextObject, ChatCompletionToolCallChunk, ChatCompletionToolCallFunctionChunk, + ChatCompletionToolChoiceFunctionParam, + ChatCompletionToolChoiceObjectParam, + ChatCompletionToolChoiceValues, + ChatCompletionToolMessage, + ChatCompletionToolParam, + ChatCompletionToolParamFunctionChunk, ChatCompletionUsageBlock, + ChatCompletionUserMessage, ) from litellm.types.utils import GenericStreamingChunk from litellm.utils import CustomStreamWrapper, ModelResponse, Usage @@ -168,6 +187,210 @@ class AnthropicConfig: optional_params["top_p"] = value return optional_params + def translatable_anthropic_params(self) -> List: + """ + Which anthropic params, we need to translate to the openai format. + """ + return ["messages", "metadata", "system", "tool_choice", "tools"] + + def translate_anthropic_messages_to_openai( + self, + messages: List[ + Union[ + AnthropicMessagesUserMessageParam, + AnthopicMessagesAssistantMessageParam, + ] + ], + ) -> List: + new_messages: List[AllMessageValues] = [] + for m in messages: + user_message: Optional[ChatCompletionUserMessage] = None + tool_message_list: List[ChatCompletionToolMessage] = [] + ## USER MESSAGE ## + if m["role"] == "user": + ## translate user message + if isinstance(m["content"], str): + user_message = ChatCompletionUserMessage( + role="user", content=m["content"] + ) + elif isinstance(m["content"], list): + new_user_content_list: List[ + Union[ChatCompletionTextObject, ChatCompletionImageObject] + ] = [] + for content in m["content"]: + if content["type"] == "text": + text_obj = ChatCompletionTextObject( + type="text", text=content["text"] + ) + new_user_content_list.append(text_obj) + elif content["type"] == "image": + image_url = ChatCompletionImageUrlObject( + url=f"data:{content['type']};base64,{content['source']}" + ) + image_obj = ChatCompletionImageObject( + type="image_url", image_url=image_url + ) + + new_user_content_list.append(image_obj) + elif content["type"] == "tool_result": + if "content" not in content: + tool_result = ChatCompletionToolMessage( + role="tool", + tool_call_id=content["tool_use_id"], + content="", + ) + tool_message_list.append(tool_result) + elif isinstance(content["content"], str): + tool_result = ChatCompletionToolMessage( + role="tool", + tool_call_id=content["tool_use_id"], + content=content["content"], + ) + tool_message_list.append(tool_result) + elif isinstance(content["content"], list): + for c in content["content"]: + if c["type"] == "text": + tool_result = ChatCompletionToolMessage( + role="tool", + tool_call_id=content["tool_use_id"], + content=c["text"], + ) + tool_message_list.append(tool_result) + elif c["type"] == "image": + image_str = ( + f"data:{c['type']};base64,{c['source']}" + ) + tool_result = ChatCompletionToolMessage( + role="tool", + tool_call_id=content["tool_use_id"], + content=image_str, + ) + tool_message_list.append(tool_result) + + if user_message is not None: + new_messages.append(user_message) + + if len(tool_message_list) > 0: + new_messages.extend(tool_message_list) + + ## ASSISTANT MESSAGE ## + assistant_message_str: Optional[str] = None + tool_calls: List[ChatCompletionAssistantToolCall] = [] + if m["role"] == "assistant": + if isinstance(m["content"], str): + assistant_message_str = m["content"] + elif isinstance(m["content"], list): + for content in m["content"]: + if content["type"] == "text": + if assistant_message_str is None: + assistant_message_str = content["text"] + else: + assistant_message_str += content["text"] + elif content["type"] == "tool_use": + function_chunk = ChatCompletionToolCallFunctionChunk( + name=content["name"], + arguments=json.dumps(content["input"]), + ) + + tool_calls.append( + ChatCompletionAssistantToolCall( + id=content["id"], + type="function", + function=function_chunk, + ) + ) + + assistant_message = ChatCompletionAssistantMessage( + role="assistant", content=assistant_message_str, tool_calls=tool_calls + ) + new_messages.append(assistant_message) + + return new_messages + + def translate_anthropic_tool_choice_to_openai( + self, tool_choice: AnthropicMessagesToolChoice + ) -> ChatCompletionToolChoiceValues: + if tool_choice["type"] == "any": + return "required" + elif tool_choice["type"] == "auto": + return "auto" + elif tool_choice["type"] == "tool": + tc_function_param = ChatCompletionToolChoiceFunctionParam( + name=tool_choice.get("name", "") + ) + return ChatCompletionToolChoiceObjectParam( + type="function", function=tc_function_param + ) + else: + raise ValueError( + "Incompatible tool choice param submitted - {}".format(tool_choice) + ) + + def translate_anthropic_tools_to_openai( + self, tools: List[AnthropicMessagesTool] + ) -> List[ChatCompletionToolParam]: + new_tools: List[ChatCompletionToolParam] = [] + for tool in tools: + function_chunk = ChatCompletionToolParamFunctionChunk( + name=tool["name"], + parameters=tool["input_schema"], + ) + if "description" in tool: + function_chunk["description"] = tool["description"] + new_tools.append( + ChatCompletionToolParam(type="function", function=function_chunk) + ) + + return new_tools + + def translate_anthropic_to_openai( + self, anthropic_message_request: AnthropicMessagesRequest + ) -> ChatCompletionRequest: + """ + This is used by the beta Anthropic Adapter, for translating anthropic `/v1/messages` requests to the openai format. + """ + new_messages: List[AllMessageValues] = [] + + ## CONVERT ANTHROPIC MESSAGES TO OPENAI + new_messages = self.translate_anthropic_messages_to_openai( + messages=anthropic_message_request["messages"] + ) + ## ADD SYSTEM MESSAGE TO MESSAGES + if "system" in anthropic_message_request: + new_messages.insert( + 0, + ChatCompletionSystemMessage( + role="system", content=anthropic_message_request["system"] + ), + ) + + new_kwargs: ChatCompletionRequest = { + "model": anthropic_message_request["model"], + "messages": new_messages, + } + ## CONVERT METADATA (user_id) + if "metadata" in anthropic_message_request: + if "user_id" in anthropic_message_request["metadata"]: + new_kwargs["user"] = anthropic_message_request["metadata"]["user_id"] + + ## CONVERT TOOL CHOICE + if "tool_choice" in anthropic_message_request: + new_kwargs["tool_choice"] = self.translate_anthropic_tool_choice_to_openai( + tool_choice=anthropic_message_request["tool_choice"] + ) + ## CONVERT TOOLS + if "tools" in anthropic_message_request: + new_kwargs["tools"] = self.translate_anthropic_tools_to_openai( + tools=anthropic_message_request["tools"] + ) + + translatable_params = self.translatable_anthropic_params() + for k, v in anthropic_message_request.items(): + if k not in translatable_params: # pass remaining params as is + new_kwargs[k] = v # type: ignore + + return new_kwargs + # makes headers for API call def validate_environment(api_key, user_headers): diff --git a/litellm/main.py b/litellm/main.py index 43e6ad3fc70..bb203ae4a96 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -48,6 +48,7 @@ from litellm import ( # type: ignore get_litellm_params, get_optional_params, ) +from litellm.integrations.custom_logger import CustomLogger from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.utils import ( CustomStreamWrapper, @@ -3943,6 +3944,33 @@ def text_completion( return text_completion_response +###### Adapter Completion ################ + + +def adapter_completion(*, adapter_id: str, **kwargs) -> Any: + translation_obj: Optional[CustomLogger] = None + for item in litellm.adapters: + if item["id"] == adapter_id: + translation_obj = item["adapter"] + + if translation_obj is None: + raise ValueError( + "No matching adapter given. Received 'adapter_id'={}, litellm.adapters={}".format( + adapter_id, litellm.adapters + ) + ) + + new_kwargs = translation_obj.translate_completion_input_params(kwargs=kwargs) + + response: ModelResponse = completion(**new_kwargs) # type: ignore + + translated_response = translation_obj.translate_completion_output_params( + response=response + ) + + return translated_response + + ##### Moderation ####################### diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 8f8b7fda03d..49db7c3787e 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -2,17 +2,6 @@ model_list: - model_name: "*" litellm_params: model: "openai/*" - - model_name: gemini-1.5-flash - litellm_params: - model: gemini/gemini-1.5-flash - - model_name: whisper - litellm_params: - model: azure/azure-whisper - api_version: 2024-02-15-preview - api_base: os.environ/AZURE_EUROPE_API_BASE - api_key: os.environ/AZURE_EUROPE_API_KEY - model_info: - mode: audio_transcription diff --git a/litellm/tests/test_anthropic_completion.py b/litellm/tests/test_anthropic_completion.py index 674d0907623..25d5823c324 100644 --- a/litellm/tests/test_anthropic_completion.py +++ b/litellm/tests/test_anthropic_completion.py @@ -1,4203 +1,35 @@ # What is this? ## Unit tests for Anthropic Adapter -# import asyncio -# import os -# import sys -# import traceback +import asyncio +import os +import sys +import traceback -# from dotenv import load_dotenv +from dotenv import load_dotenv -# load_dotenv() -# import io -# import os +load_dotenv() +import io +import os -# sys.path.insert( -# 0, os.path.abspath("../..") -# ) # Adds the parent directory to the system path -# from unittest.mock import MagicMock, patch +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system path +from unittest.mock import MagicMock, patch -# import pytest +import pytest -# import litellm -# from litellm import ( -# RateLimitError, -# TextCompletionResponse, -# atext_completion, -# completion, -# completion_cost, -# embedding, -# text_completion, -# ) +import litellm +from litellm import adapter_completion +from litellm.adapters.anthropic_adapter import anthropic_adapter -# litellm.num_retries = 3 +def test_anthropic_completion(): + litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}] -# token_prompt = [ -# [ -# 32, -# 2043, -# 32, -# 329, -# 4585, -# 262, -# 1644, -# 14, -# 34, -# 3705, -# 319, -# 616, -# 47551, -# 30, -# 930, -# 19219, -# 284, -# 1949, -# 284, -# 787, -# 428, -# 355, -# 1790, -# 355, -# 1744, -# 981, -# 1390, -# 3307, -# 2622, -# 13, -# 220, -# 198, -# 198, -# 40, -# 423, -# 587, -# 351, -# 616, -# 41668, -# 32682, -# 329, -# 718, -# 812, -# 13, -# 376, -# 666, -# 32682, -# 468, -# 281, -# 4697, -# 6621, -# 11, -# 356, -# 1183, -# 869, -# 607, -# 25737, -# 11, -# 508, -# 318, -# 2579, -# 290, -# 468, -# 257, -# 642, -# 614, -# 1468, -# 1200, -# 13, -# 314, -# 373, -# 612, -# 262, -# 1110, -# 25737, -# 373, -# 287, -# 4827, -# 290, -# 14801, -# 373, -# 4642, -# 11, -# 673, -# 318, -# 616, -# 41803, -# 13, -# 2399, -# 2104, -# 1641, -# 468, -# 6412, -# 284, -# 502, -# 355, -# 465, -# 38074, -# 494, -# 1201, -# 1110, -# 352, -# 13, -# 314, -# 716, -# 407, -# 2910, -# 475, -# 356, -# 389, -# 1641, -# 11, -# 673, -# 3848, -# 502, -# 38074, -# 494, -# 290, -# 356, -# 423, -# 3993, -# 13801, -# 11, -# 26626, -# 11864, -# 11, -# 3503, -# 13, -# 220, -# 198, -# 198, -# 17, -# 812, -# 2084, -# 25737, -# 373, -# 287, -# 14321, -# 422, -# 2563, -# 13230, -# 13, -# 21051, -# 11, -# 2356, -# 25542, -# 11, -# 290, -# 47482, -# 897, -# 547, -# 607, -# 1517, -# 13, -# 1375, -# 550, -# 257, -# 5110, -# 14608, -# 290, -# 262, -# 1641, -# 7723, -# 1637, -# 284, -# 3758, -# 607, -# 284, -# 14321, -# 290, -# 477, -# 8389, -# 257, -# 7269, -# 284, -# 1011, -# 1337, -# 286, -# 14801, -# 13, -# 383, -# 5156, -# 338, -# 9955, -# 11, -# 25737, -# 338, -# 13850, -# 11, -# 468, -# 257, -# 47973, -# 14, -# 9979, -# 2762, -# 1693, -# 290, -# 373, -# 503, -# 286, -# 3240, -# 329, -# 362, -# 1933, -# 523, -# 339, -# 2492, -# 470, -# 612, -# 329, -# 477, -# 286, -# 428, -# 13, -# 220, -# 198, -# 198, -# 3347, -# 10667, -# 5223, -# 503, -# 706, -# 513, -# 1528, -# 11, -# 23630, -# 673, -# 373, -# 366, -# 38125, -# 290, -# 655, -# 2622, -# 257, -# 3338, -# 8399, -# 1911, -# 314, -# 2298, -# 607, -# 510, -# 11, -# 1011, -# 607, -# 284, -# 607, -# 2156, -# 11, -# 290, -# 673, -# 3393, -# 2925, -# 284, -# 7523, -# 20349, -# 290, -# 4144, -# 257, -# 6099, -# 13, -# 314, -# 836, -# 470, -# 892, -# 20349, -# 318, -# 257, -# 2563, -# 290, -# 716, -# 845, -# 386, -# 12, -# 66, -# 1236, -# 571, -# 292, -# 3584, -# 314, -# 836, -# 470, -# 7523, -# 11, -# 475, -# 326, -# 373, -# 407, -# 5035, -# 6402, -# 314, -# 655, -# 6497, -# 607, -# 510, -# 422, -# 14321, -# 13, -# 220, -# 198, -# 198, -# 32, -# 1285, -# 1568, -# 673, -# 373, -# 6294, -# 329, -# 3013, -# 24707, -# 287, -# 262, -# 12436, -# 1539, -# 819, -# 5722, -# 329, -# 852, -# 604, -# 1933, -# 2739, -# 11, -# 39398, -# 607, -# 1097, -# 5059, -# 981, -# 1029, -# 290, -# 318, -# 852, -# 16334, -# 329, -# 720, -# 1120, -# 74, -# 422, -# 15228, -# 278, -# 656, -# 257, -# 2156, -# 11, -# 290, -# 373, -# 12165, -# 503, -# 286, -# 376, -# 666, -# 32682, -# 338, -# 584, -# 6621, -# 338, -# 2156, -# 329, -# 32012, -# 262, -# 14595, -# 373, -# 30601, -# 510, -# 290, -# 2491, -# 357, -# 7091, -# 373, -# 1029, -# 8, -# 290, -# 262, -# 2104, -# 34624, -# 373, -# 46432, -# 1268, -# 1961, -# 422, -# 1660, -# 2465, -# 780, -# 8168, -# 2073, -# 1625, -# 1363, -# 329, -# 807, -# 2250, -# 13, -# 720, -# 1238, -# 11, -# 830, -# 286, -# 2465, -# 290, -# 5875, -# 5770, -# 511, -# 2156, -# 5096, -# 5017, -# 340, -# 13, -# 220, -# 198, -# 198, -# 2504, -# 373, -# 477, -# 938, -# 614, -# 13, -# 1119, -# 1053, -# 587, -# 287, -# 511, -# 649, -# 2156, -# 319, -# 511, -# 898, -# 329, -# 546, -# 718, -# 1933, -# 13, -# 554, -# 3389, -# 673, -# 1444, -# 34020, -# 290, -# 531, -# 511, -# 8744, -# 373, -# 4423, -# 572, -# 780, -# 673, -# 1422, -# 470, -# 423, -# 262, -# 1637, -# 780, -# 41646, -# 338, -# 37751, -# 1392, -# 32621, -# 510, -# 290, -# 1422, -# 470, -# 467, -# 832, -# 13, -# 679, -# 3432, -# 511, -# 2739, -# 8744, -# 9024, -# 492, -# 257, -# 2472, -# 286, -# 720, -# 4059, -# 13, -# 314, -# 1807, -# 340, -# 373, -# 13678, -# 306, -# 5789, -# 475, -# 4030, -# 616, -# 5422, -# 4423, -# 13, -# 1439, -# 468, -# 587, -# 5897, -# 1201, -# 13, -# 220, -# 198, -# 198, -# 7571, -# 2745, -# 2084, -# 11, -# 673, -# 1965, -# 502, -# 284, -# 8804, -# 617, -# 1637, -# 284, -# 651, -# 38464, -# 329, -# 399, -# 8535, -# 13, -# 3226, -# 1781, -# 314, -# 1101, -# 407, -# 1016, -# 284, -# 1309, -# 616, -# 41803, -# 393, -# 6621, -# 467, -# 14720, -# 11, -# 645, -# 2300, -# 644, -# 318, -# 1016, -# 319, -# 4306, -# 11, -# 523, -# 314, -# 910, -# 314, -# 1183, -# 307, -# 625, -# 379, -# 642, -# 13, -# 314, -# 1392, -# 572, -# 670, -# 1903, -# 290, -# 651, -# 612, -# 379, -# 362, -# 25, -# 2231, -# 13, -# 314, -# 1282, -# 287, -# 1262, -# 616, -# 13952, -# 1994, -# 11, -# 2513, -# 287, -# 11, -# 766, -# 399, -# 8535, -# 2712, -# 351, -# 36062, -# 287, -# 262, -# 5228, -# 11, -# 25737, -# 3804, -# 503, -# 319, -# 262, -# 18507, -# 11, -# 290, -# 16914, -# 319, -# 262, -# 6891, -# 3084, -# 13, -# 8989, -# 2406, -# 422, -# 257, -# 1641, -# 47655, -# 351, -# 13230, -# 11, -# 314, -# 760, -# 644, -# 16914, -# 3073, -# 588, -# 13, -# 314, -# 836, -# 470, -# 760, -# 703, -# 881, -# 340, -# 373, -# 11, -# 475, -# 314, -# 714, -# 423, -# 23529, -# 276, -# 340, -# 510, -# 290, -# 5901, -# 616, -# 18057, -# 351, -# 340, -# 13, -# 314, -# 6810, -# 19772, -# 2024, -# 8347, -# 287, -# 262, -# 2166, -# 2119, -# 290, -# 399, -# 8535, -# 373, -# 287, -# 3294, -# 11685, -# 286, -# 8242, -# 290, -# 607, -# 7374, -# 15224, -# 13, -# 383, -# 4894, -# 373, -# 572, -# 13, -# 383, -# 2156, -# 373, -# 3863, -# 2319, -# 37, -# 532, -# 340, -# 373, -# 1542, -# 2354, -# 13, -# 220, -# 198, -# 198, -# 40, -# 1718, -# 399, -# 8535, -# 284, -# 616, -# 1097, -# 11, -# 290, -# 1444, -# 16679, -# 329, -# 281, -# 22536, -# 355, -# 314, -# 373, -# 12008, -# 25737, -# 373, -# 14904, -# 2752, -# 13, -# 220, -# 314, -# 1422, -# 470, -# 765, -# 284, -# 10436, -# 290, -# 22601, -# 503, -# 399, -# 8535, -# 523, -# 314, -# 9658, -# 287, -# 262, -# 1097, -# 290, -# 1309, -# 607, -# 711, -# 319, -# 616, -# 3072, -# 1566, -# 262, -# 22536, -# 5284, -# 13, -# 3226, -# 1781, -# 1644, -# 290, -# 32084, -# 3751, -# 510, -# 355, -# 880, -# 13, -# 314, -# 4893, -# 262, -# 3074, -# 290, -# 780, -# 399, -# 8535, -# 338, -# 9955, -# 318, -# 503, -# 286, -# 3240, -# 1762, -# 11, -# 34020, -# 14, -# 44, -# 4146, -# 547, -# 1444, -# 13, -# 1649, -# 484, -# 5284, -# 484, -# 547, -# 5897, -# 290, -# 4692, -# 11, -# 1422, -# 470, -# 1107, -# 1561, -# 11, -# 1718, -# 399, -# 8535, -# 11, -# 290, -# 1297, -# 502, -# 284, -# 467, -# 1363, -# 13, -# 220, -# 198, -# 198, -# 2025, -# 1711, -# 1568, -# 314, -# 651, -# 1363, -# 290, -# 41668, -# 32682, -# 7893, -# 502, -# 644, -# 314, -# 1053, -# 1760, -# 13, -# 314, -# 4893, -# 2279, -# 284, -# 683, -# 290, -# 477, -# 339, -# 550, -# 373, -# 8993, -# 329, -# 502, -# 13, -# 18626, -# 262, -# 2104, -# 1641, -# 1541, -# 2993, -# 290, -# 547, -# 28674, -# 379, -# 502, -# 329, -# 644, -# 314, -# 550, -# 1760, -# 13, -# 18626, -# 314, -# 373, -# 366, -# 448, -# 286, -# 1627, -# 290, -# 8531, -# 1, -# 780, -# 314, -# 1444, -# 16679, -# 878, -# 4379, -# 611, -# 673, -# 373, -# 1682, -# 31245, -# 6, -# 278, -# 780, -# 340, -# 2900, -# 503, -# 673, -# 373, -# 655, -# 47583, -# 503, -# 422, -# 262, -# 16914, -# 13, -# 775, -# 8350, -# 329, -# 2250, -# 290, -# 314, -# 1364, -# 290, -# 3377, -# 262, -# 1755, -# 379, -# 616, -# 1266, -# 1545, -# 338, -# 2156, -# 290, -# 16896, -# 477, -# 1755, -# 13, -# 314, -# 3521, -# 470, -# 5412, -# 340, -# 477, -# 523, -# 314, -# 2900, -# 616, -# 3072, -# 572, -# 290, -# 3088, -# 284, -# 8960, -# 290, -# 655, -# 9480, -# 866, -# 13, -# 2011, -# 1266, -# 1545, -# 373, -# 510, -# 477, -# 1755, -# 351, -# 502, -# 11, -# 5149, -# 502, -# 314, -# 750, -# 2147, -# 2642, -# 11, -# 290, -# 314, -# 1101, -# 8788, -# 13, -# 220, -# 198, -# 198, -# 40, -# 1210, -# 616, -# 3072, -# 319, -# 290, -# 314, -# 550, -# 6135, -# 13399, -# 14, -# 37348, -# 1095, -# 13, -# 31515, -# 11, -# 34020, -# 11, -# 47551, -# 11, -# 41668, -# 32682, -# 11, -# 290, -# 511, -# 7083, -# 1641, -# 1866, -# 24630, -# 502, -# 13, -# 1119, -# 389, -# 2282, -# 314, -# 20484, -# 607, -# 1204, -# 11, -# 20484, -# 399, -# 8535, -# 338, -# 1204, -# 11, -# 925, -# 2279, -# 517, -# 8253, -# 621, -# 340, -# 2622, -# 284, -# 307, -# 11, -# 925, -# 340, -# 1171, -# 618, -# 340, -# 373, -# 257, -# 366, -# 17989, -# 14669, -# 1600, -# 290, -# 20484, -# 25737, -# 338, -# 8395, -# 286, -# 1683, -# 1972, -# 20750, -# 393, -# 1719, -# 10804, -# 286, -# 607, -# 1200, -# 757, -# 11, -# 4844, -# 286, -# 606, -# 1683, -# 765, -# 284, -# 766, -# 502, -# 757, -# 290, -# 314, -# 481, -# 1239, -# 766, -# 616, -# 41803, -# 757, -# 11, -# 290, -# 484, -# 765, -# 502, -# 284, -# 1414, -# 329, -# 25737, -# 338, -# 7356, -# 6314, -# 290, -# 20889, -# 502, -# 329, -# 262, -# 32084, -# 1339, -# 290, -# 7016, -# 12616, -# 13, -# 198, -# 198, -# 40, -# 716, -# 635, -# 783, -# 2060, -# 13, -# 1406, -# 319, -# 1353, -# 286, -# 6078, -# 616, -# 1266, -# 1545, -# 286, -# 838, -# 812, -# 357, -# 69, -# 666, -# 32682, -# 828, -# 314, -# 481, -# 4425, -# 616, -# 7962, -# 314, -# 550, -# 351, -# 683, -# 11, -# 644, -# 314, -# 3177, -# 616, -# 1641, -# 11, -# 290, -# 616, -# 399, -# 8535, -# 13, -# 198, -# 198, -# 40, -# 4988, -# 1254, -# 12361, -# 13, -# 314, -# 423, -# 12361, -# 9751, -# 284, -# 262, -# 966, -# 810, -# 314, -# 1101, -# 7960, -# 2130, -# 318, -# 1016, -# 284, -# 1282, -# 651, -# 366, -# 260, -# 18674, -# 1, -# 319, -# 502, -# 329, -# 644, -# 314, -# 750, -# 13, -# 314, -# 460, -# 470, -# 4483, -# 13, -# 314, -# 423, -# 2626, -# 767, -# 8059, -# 422, -# 340, -# 13, -# 314, -# 1101, -# 407, -# 11029, -# 329, -# 7510, -# 13, -# 314, -# 423, -# 11668, -# 739, -# 616, -# 2951, -# 13, -# 314, -# 1053, -# 550, -# 807, -# 50082, -# 12, -# 12545, -# 287, -# 734, -# 2745, -# 13, -# 1629, -# 717, -# 314, -# 2936, -# 523, -# 6563, -# 287, -# 616, -# 2551, -# 475, -# 355, -# 262, -# 1528, -# 467, -# 416, -# 314, -# 1101, -# 3612, -# 3863, -# 484, -# 547, -# 826, -# 290, -# 314, -# 815, -# 423, -# 10667, -# 319, -# 607, -# 878, -# 4585, -# 16679, -# 290, -# 852, -# 5306, -# 3019, -# 992, -# 13, -# 314, -# 836, -# 470, -# 1337, -# 546, -# 25737, -# 7471, -# 11, -# 475, -# 314, -# 750, -# 18344, -# 257, -# 642, -# 614, -# 1468, -# 1200, -# 1497, -# 422, -# 607, -# 3397, -# 290, -# 314, -# 1254, -# 12361, -# 546, -# 340, -# 13, -# 314, -# 760, -# 2130, -# 287, -# 262, -# 1641, -# 481, -# 1011, -# 607, -# 287, -# 11, -# 475, -# 340, -# 338, -# 1239, -# 588, -# 852, -# 351, -# 534, -# 3397, -# 13, -# 1375, -# 481, -# 1663, -# 510, -# 20315, -# 278, -# 502, -# 329, -# 340, -# 290, -# 477, -# 314, -# 1053, -# 1683, -# 1760, -# 318, -# 1842, -# 607, -# 355, -# 616, -# 898, -# 13, -# 220, -# 198, -# 198, -# 22367, -# 11, -# 317, -# 2043, -# 32, -# 30, -# 4222, -# 1037, -# 502, -# 13, -# 383, -# 14934, -# 318, -# 6600, -# 502, -# 6776, -# 13, -# 220, -# 198, -# 24361, -# 25, -# 1148, -# 428, -# 2642, -# 30, -# 198, -# 33706, -# 25, -# 645, -# ], -# [ -# 32, -# 2043, -# 32, -# 329, -# 4585, -# 262, -# 1644, -# 14, -# 34, -# 3705, -# 319, -# 616, -# 47551, -# 30, -# 930, -# 19219, -# 284, -# 1949, -# 284, -# 787, -# 428, -# 355, -# 1790, -# 355, -# 1744, -# 981, -# 1390, -# 3307, -# 2622, -# 13, -# 220, -# 198, -# 198, -# 40, -# 423, -# 587, -# 351, -# 616, -# 41668, -# 32682, -# 329, -# 718, -# 812, -# 13, -# 376, -# 666, -# 32682, -# 468, -# 281, -# 4697, -# 6621, -# 11, -# 356, -# 1183, -# 869, -# 607, -# 25737, -# 11, -# 508, -# 318, -# 2579, -# 290, -# 468, -# 257, -# 642, -# 614, -# 1468, -# 1200, -# 13, -# 314, -# 373, -# 612, -# 262, -# 1110, -# 25737, -# 373, -# 287, -# 4827, -# 290, -# 14801, -# 373, -# 4642, -# 11, -# 673, -# 318, -# 616, -# 41803, -# 13, -# 2399, -# 2104, -# 1641, -# 468, -# 6412, -# 284, -# 502, -# 355, -# 465, -# 38074, -# 494, -# 1201, -# 1110, -# 352, -# 13, -# 314, -# 716, -# 407, -# 2910, -# 475, -# 356, -# 389, -# 1641, -# 11, -# 673, -# 3848, -# 502, -# 38074, -# 494, -# 290, -# 356, -# 423, -# 3993, -# 13801, -# 11, -# 26626, -# 11864, -# 11, -# 3503, -# 13, -# 220, -# 198, -# 198, -# 17, -# 812, -# 2084, -# 25737, -# 373, -# 287, -# 14321, -# 422, -# 2563, -# 13230, -# 13, -# 21051, -# 11, -# 2356, -# 25542, -# 11, -# 290, -# 47482, -# 897, -# 547, -# 607, -# 1517, -# 13, -# 1375, -# 550, -# 257, -# 5110, -# 14608, -# 290, -# 262, -# 1641, -# 7723, -# 1637, -# 284, -# 3758, -# 607, -# 284, -# 14321, -# 290, -# 477, -# 8389, -# 257, -# 7269, -# 284, -# 1011, -# 1337, -# 286, -# 14801, -# 13, -# 383, -# 5156, -# 338, -# 9955, -# 11, -# 25737, -# 338, -# 13850, -# 11, -# 468, -# 257, -# 47973, -# 14, -# 9979, -# 2762, -# 1693, -# 290, -# 373, -# 503, -# 286, -# 3240, -# 329, -# 362, -# 1933, -# 523, -# 339, -# 2492, -# 470, -# 612, -# 329, -# 477, -# 286, -# 428, -# 13, -# 220, -# 198, -# 198, -# 3347, -# 10667, -# 5223, -# 503, -# 706, -# 513, -# 1528, -# 11, -# 23630, -# 673, -# 373, -# 366, -# 38125, -# 290, -# 655, -# 2622, -# 257, -# 3338, -# 8399, -# 1911, -# 314, -# 2298, -# 607, -# 510, -# 11, -# 1011, -# 607, -# 284, -# 607, -# 2156, -# 11, -# 290, -# 673, -# 3393, -# 2925, -# 284, -# 7523, -# 20349, -# 290, -# 4144, -# 257, -# 6099, -# 13, -# 314, -# 836, -# 470, -# 892, -# 20349, -# 318, -# 257, -# 2563, -# 290, -# 716, -# 845, -# 386, -# 12, -# 66, -# 1236, -# 571, -# 292, -# 3584, -# 314, -# 836, -# 470, -# 7523, -# 11, -# 475, -# 326, -# 373, -# 407, -# 5035, -# 6402, -# 314, -# 655, -# 6497, -# 607, -# 510, -# 422, -# 14321, -# 13, -# 220, -# 198, -# 198, -# 32, -# 1285, -# 1568, -# 673, -# 373, -# 6294, -# 329, -# 3013, -# 24707, -# 287, -# 262, -# 12436, -# 1539, -# 819, -# 5722, -# 329, -# 852, -# 604, -# 1933, -# 2739, -# 11, -# 39398, -# 607, -# 1097, -# 5059, -# 981, -# 1029, -# 290, -# 318, -# 852, -# 16334, -# 329, -# 720, -# 1120, -# 74, -# 422, -# 15228, -# 278, -# 656, -# 257, -# 2156, -# 11, -# 290, -# 373, -# 12165, -# 503, -# 286, -# 376, -# 666, -# 32682, -# 338, -# 584, -# 6621, -# 338, -# 2156, -# 329, -# 32012, -# 262, -# 14595, -# 373, -# 30601, -# 510, -# 290, -# 2491, -# 357, -# 7091, -# 373, -# 1029, -# 8, -# 290, -# 262, -# 2104, -# 34624, -# 373, -# 46432, -# 1268, -# 1961, -# 422, -# 1660, -# 2465, -# 780, -# 8168, -# 2073, -# 1625, -# 1363, -# 329, -# 807, -# 2250, -# 13, -# 720, -# 1238, -# 11, -# 830, -# 286, -# 2465, -# 290, -# 5875, -# 5770, -# 511, -# 2156, -# 5096, -# 5017, -# 340, -# 13, -# 220, -# 198, -# 198, -# 2504, -# 373, -# 477, -# 938, -# 614, -# 13, -# 1119, -# 1053, -# 587, -# 287, -# 511, -# 649, -# 2156, -# 319, -# 511, -# 898, -# 329, -# 546, -# 718, -# 1933, -# 13, -# 554, -# 3389, -# 673, -# 1444, -# 34020, -# 290, -# 531, -# 511, -# 8744, -# 373, -# 4423, -# 572, -# 780, -# 673, -# 1422, -# 470, -# 423, -# 262, -# 1637, -# 780, -# 41646, -# 338, -# 37751, -# 1392, -# 32621, -# 510, -# 290, -# 1422, -# 470, -# 467, -# 832, -# 13, -# 679, -# 3432, -# 511, -# 2739, -# 8744, -# 9024, -# 492, -# 257, -# 2472, -# 286, -# 720, -# 4059, -# 13, -# 314, -# 1807, -# 340, -# 373, -# 13678, -# 306, -# 5789, -# 475, -# 4030, -# 616, -# 5422, -# 4423, -# 13, -# 1439, -# 468, -# 587, -# 5897, -# 1201, -# 13, -# 220, -# 198, -# 198, -# 7571, -# 2745, -# 2084, -# 11, -# 673, -# 1965, -# 502, -# 284, -# 8804, -# 617, -# 1637, -# 284, -# 651, -# 38464, -# 329, -# 399, -# 8535, -# 13, -# 3226, -# 1781, -# 314, -# 1101, -# 407, -# 1016, -# 284, -# 1309, -# 616, -# 41803, -# 393, -# 6621, -# 467, -# 14720, -# 11, -# 645, -# 2300, -# 644, -# 318, -# 1016, -# 319, -# 4306, -# 11, -# 523, -# 314, -# 910, -# 314, -# 1183, -# 307, -# 625, -# 379, -# 642, -# 13, -# 314, -# 1392, -# 572, -# 670, -# 1903, -# 290, -# 651, -# 612, -# 379, -# 362, -# 25, -# 2231, -# 13, -# 314, -# 1282, -# 287, -# 1262, -# 616, -# 13952, -# 1994, -# 11, -# 2513, -# 287, -# 11, -# 766, -# 399, -# 8535, -# 2712, -# 351, -# 36062, -# 287, -# 262, -# 5228, -# 11, -# 25737, -# 3804, -# 503, -# 319, -# 262, -# 18507, -# 11, -# 290, -# 16914, -# 319, -# 262, -# 6891, -# 3084, -# 13, -# 8989, -# 2406, -# 422, -# 257, -# 1641, -# 47655, -# 351, -# 13230, -# 11, -# 314, -# 760, -# 644, -# 16914, -# 3073, -# 588, -# 13, -# 314, -# 836, -# 470, -# 760, -# 703, -# 881, -# 340, -# 373, -# 11, -# 475, -# 314, -# 714, -# 423, -# 23529, -# 276, -# 340, -# 510, -# 290, -# 5901, -# 616, -# 18057, -# 351, -# 340, -# 13, -# 314, -# 6810, -# 19772, -# 2024, -# 8347, -# 287, -# 262, -# 2166, -# 2119, -# 290, -# 399, -# 8535, -# 373, -# 287, -# 3294, -# 11685, -# 286, -# 8242, -# 290, -# 607, -# 7374, -# 15224, -# 13, -# 383, -# 4894, -# 373, -# 572, -# 13, -# 383, -# 2156, -# 373, -# 3863, -# 2319, -# 37, -# 532, -# 340, -# 373, -# 1542, -# 2354, -# 13, -# 220, -# 198, -# 198, -# 40, -# 1718, -# 399, -# 8535, -# 284, -# 616, -# 1097, -# 11, -# 290, -# 1444, -# 16679, -# 329, -# 281, -# 22536, -# 355, -# 314, -# 373, -# 12008, -# 25737, -# 373, -# 14904, -# 2752, -# 13, -# 220, -# 314, -# 1422, -# 470, -# 765, -# 284, -# 10436, -# 290, -# 22601, -# 503, -# 399, -# 8535, -# 523, -# 314, -# 9658, -# 287, -# 262, -# 1097, -# 290, -# 1309, -# 607, -# 711, -# 319, -# 616, -# 3072, -# 1566, -# 262, -# 22536, -# 5284, -# 13, -# 3226, -# 1781, -# 1644, -# 290, -# 32084, -# 3751, -# 510, -# 355, -# 880, -# 13, -# 314, -# 4893, -# 262, -# 3074, -# 290, -# 780, -# 399, -# 8535, -# 338, -# 9955, -# 318, -# 503, -# 286, -# 3240, -# 1762, -# 11, -# 34020, -# 14, -# 44, -# 4146, -# 547, -# 1444, -# 13, -# 1649, -# 484, -# 5284, -# 484, -# 547, -# 5897, -# 290, -# 4692, -# 11, -# 1422, -# 470, -# 1107, -# 1561, -# 11, -# 1718, -# 399, -# 8535, -# 11, -# 290, -# 1297, -# 502, -# 284, -# 467, -# 1363, -# 13, -# 220, -# 198, -# 198, -# 2025, -# 1711, -# 1568, -# 314, -# 651, -# 1363, -# 290, -# 41668, -# 32682, -# 7893, -# 502, -# 644, -# 314, -# 1053, -# 1760, -# 13, -# 314, -# 4893, -# 2279, -# 284, -# 683, -# 290, -# 477, -# 339, -# 550, -# 373, -# 8993, -# 329, -# 502, -# 13, -# 18626, -# 262, -# 2104, -# 1641, -# 1541, -# 2993, -# 290, -# 547, -# 28674, -# 379, -# 502, -# 329, -# 644, -# 314, -# 550, -# 1760, -# 13, -# 18626, -# 314, -# 373, -# 366, -# 448, -# 286, -# 1627, -# 290, -# 8531, -# 1, -# 780, -# 314, -# 1444, -# 16679, -# 878, -# 4379, -# 611, -# 673, -# 373, -# 1682, -# 31245, -# 6, -# 278, -# 780, -# 340, -# 2900, -# 503, -# 673, -# 373, -# 655, -# 47583, -# 503, -# 422, -# 262, -# 16914, -# 13, -# 775, -# 8350, -# 329, -# 2250, -# 290, -# 314, -# 1364, -# 290, -# 3377, -# 262, -# 1755, -# 379, -# 616, -# 1266, -# 1545, -# 338, -# 2156, -# 290, -# 16896, -# 477, -# 1755, -# 13, -# 314, -# 3521, -# 470, -# 5412, -# 340, -# 477, -# 523, -# 314, -# 2900, -# 616, -# 3072, -# 572, -# 290, -# 3088, -# 284, -# 8960, -# 290, -# 655, -# 9480, -# 866, -# 13, -# 2011, -# 1266, -# 1545, -# 373, -# 510, -# 477, -# 1755, -# 351, -# 502, -# 11, -# 5149, -# 502, -# 314, -# 750, -# 2147, -# 2642, -# 11, -# 290, -# 314, -# 1101, -# 8788, -# 13, -# 220, -# 198, -# 198, -# 40, -# 1210, -# 616, -# 3072, -# 319, -# 290, -# 314, -# 550, -# 6135, -# 13399, -# 14, -# 37348, -# 1095, -# 13, -# 31515, -# 11, -# 34020, -# 11, -# 47551, -# 11, -# 41668, -# 32682, -# 11, -# 290, -# 511, -# 7083, -# 1641, -# 1866, -# 24630, -# 502, -# 13, -# 1119, -# 389, -# 2282, -# 314, -# 20484, -# 607, -# 1204, -# 11, -# 20484, -# 399, -# 8535, -# 338, -# 1204, -# 11, -# 925, -# 2279, -# 517, -# 8253, -# 621, -# 340, -# 2622, -# 284, -# 307, -# 11, -# 925, -# 340, -# 1171, -# 618, -# 340, -# 373, -# 257, -# 366, -# 17989, -# 14669, -# 1600, -# 290, -# 20484, -# 25737, -# 338, -# 8395, -# 286, -# 1683, -# 1972, -# 20750, -# 393, -# 1719, -# 10804, -# 286, -# 607, -# 1200, -# 757, -# 11, -# 4844, -# 286, -# 606, -# 1683, -# 765, -# 284, -# 766, -# 502, -# 757, -# 290, -# 314, -# 481, -# 1239, -# 766, -# 616, -# 41803, -# 757, -# 11, -# 290, -# 484, -# 765, -# 502, -# 284, -# 1414, -# 329, -# 25737, -# 338, -# 7356, -# 6314, -# 290, -# 20889, -# 502, -# 329, -# 262, -# 32084, -# 1339, -# 290, -# 7016, -# 12616, -# 13, -# 198, -# 198, -# 40, -# 716, -# 635, -# 783, -# 2060, -# 13, -# 1406, -# 319, -# 1353, -# 286, -# 6078, -# 616, -# 1266, -# 1545, -# 286, -# 838, -# 812, -# 357, -# 69, -# 666, -# 32682, -# 828, -# 314, -# 481, -# 4425, -# 616, -# 7962, -# 314, -# 550, -# 351, -# 683, -# 11, -# 644, -# 314, -# 3177, -# 616, -# 1641, -# 11, -# 290, -# 616, -# 399, -# 8535, -# 13, -# 198, -# 198, -# 40, -# 4988, -# 1254, -# 12361, -# 13, -# 314, -# 423, -# 12361, -# 9751, -# 284, -# 262, -# 966, -# 810, -# 314, -# 1101, -# 7960, -# 2130, -# 318, -# 1016, -# 284, -# 1282, -# 651, -# 366, -# 260, -# 18674, -# 1, -# 319, -# 502, -# 329, -# 644, -# 314, -# 750, -# 13, -# 314, -# 460, -# 470, -# 4483, -# 13, -# 314, -# 423, -# 2626, -# 767, -# 8059, -# 422, -# 340, -# 13, -# 314, -# 1101, -# 407, -# 11029, -# 329, -# 7510, -# 13, -# 314, -# 423, -# 11668, -# 739, -# 616, -# 2951, -# 13, -# 314, -# 1053, -# 550, -# 807, -# 50082, -# 12, -# 12545, -# 287, -# 734, -# 2745, -# 13, -# 1629, -# 717, -# 314, -# 2936, -# 523, -# 6563, -# 287, -# 616, -# 2551, -# 475, -# 355, -# 262, -# 1528, -# 467, -# 416, -# 314, -# 1101, -# 3612, -# 3863, -# 484, -# 547, -# 826, -# 290, -# 314, -# 815, -# 423, -# 10667, -# 319, -# 607, -# 878, -# 4585, -# 16679, -# 290, -# 852, -# 5306, -# 3019, -# 992, -# 13, -# 314, -# 836, -# 470, -# 1337, -# 546, -# 25737, -# 7471, -# 11, -# 475, -# 314, -# 750, -# 18344, -# 257, -# 642, -# 614, -# 1468, -# 1200, -# 1497, -# 422, -# 607, -# 3397, -# 290, -# 314, -# 1254, -# 12361, -# 546, -# 340, -# 13, -# 314, -# 760, -# 2130, -# 287, -# 262, -# 1641, -# 481, -# 1011, -# 607, -# 287, -# 11, -# 475, -# 340, -# 338, -# 1239, -# 588, -# 852, -# 351, -# 534, -# 3397, -# 13, -# 1375, -# 481, -# 1663, -# 510, -# 20315, -# 278, -# 502, -# 329, -# 340, -# 290, -# 477, -# 314, -# 1053, -# 1683, -# 1760, -# 318, -# 1842, -# 607, -# 355, -# 616, -# 898, -# 13, -# 220, -# 198, -# 198, -# 22367, -# 11, -# 317, -# 2043, -# 32, -# 30, -# 4222, -# 1037, -# 502, -# 13, -# 383, -# 14934, -# 318, -# 6600, -# 502, -# 6776, -# 13, -# 220, -# 198, -# 24361, -# 25, -# 1148, -# 428, -# 2642, -# 30, -# 198, -# 33706, -# 25, -# 3763, -# ], -# ] + messages = [{"role": "user", "content": "Hey, how's it going?"}] + response = adapter_completion( + model="gpt-3.5-turbo", messages=messages, adapter_id="anthropic" + ) - -# def test_unit_test_text_completion_object(): -# openai_object = { -# "id": "cmpl-99y7B2svVoRWe1xd7UFRmeGjZrFSh", -# "choices": [ -# { -# "finish_reason": "length", -# "index": 0, -# "logprobs": { -# "text_offset": [101], -# "token_logprobs": [-0.00023488728], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00023488728, -# "1": -8.375235, -# "zero": -14.101797, -# "__": -14.554922, -# "00": -14.98461, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 1, -# "logprobs": { -# "text_offset": [116], -# "token_logprobs": [-0.013745008], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.013745008, -# "1": -4.294995, -# "00": -12.287183, -# "2": -12.771558, -# "3": -14.013745, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 2, -# "logprobs": { -# "text_offset": [108], -# "token_logprobs": [-3.655073e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -3.655073e-5, -# "1": -10.656286, -# "__": -11.789099, -# "false": -12.984411, -# "00": -14.039099, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 3, -# "logprobs": { -# "text_offset": [106], -# "token_logprobs": [-0.1345946], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.1345946, -# "1": -2.0720947, -# "2": -12.798657, -# "false": -13.970532, -# "00": -14.27522, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 4, -# "logprobs": { -# "text_offset": [95], -# "token_logprobs": [-0.10491652], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.10491652, -# "1": -2.3236666, -# "2": -7.0111666, -# "3": -7.987729, -# "4": -9.050229, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 5, -# "logprobs": { -# "text_offset": [121], -# "token_logprobs": [-0.00026300468], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00026300468, -# "1": -8.250263, -# "zero": -14.976826, -# " ": -15.461201, -# "000": -15.773701, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 6, -# "logprobs": { -# "text_offset": [146], -# "token_logprobs": [-5.085517e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -5.085517e-5, -# "1": -9.937551, -# "000": -13.929738, -# "__": -14.968801, -# "zero": -15.070363, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 7, -# "logprobs": { -# "text_offset": [100], -# "token_logprobs": [-0.13875218], -# "tokens": ["1"], -# "top_logprobs": [ -# { -# "1": -0.13875218, -# "0": -2.0450022, -# "2": -9.7559395, -# "3": -11.1465645, -# "4": -11.5528145, -# } -# ], -# }, -# "text": "1", -# }, -# { -# "finish_reason": "length", -# "index": 8, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0005573204], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0005573204, -# "1": -7.6099324, -# "3": -10.070869, -# "2": -11.617744, -# " ": -12.859932, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 9, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0018747397], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0018747397, -# "1": -6.29875, -# "3": -11.2675, -# "4": -11.634687, -# "2": -11.822187, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 10, -# "logprobs": { -# "text_offset": [110], -# "token_logprobs": [-0.003476763], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.003476763, -# "1": -5.6909766, -# "__": -10.526915, -# "None": -10.925352, -# "False": -11.88629, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 11, -# "logprobs": { -# "text_offset": [106], -# "token_logprobs": [-0.00032962486], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00032962486, -# "1": -8.03158, -# "__": -13.445642, -# "2": -13.828455, -# "zero": -15.453455, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 12, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-9.984788e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -9.984788e-5, -# "1": -9.21885, -# " ": -14.836038, -# "zero": -16.265724, -# "00": -16.578224, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 13, -# "logprobs": { -# "text_offset": [106], -# "token_logprobs": [-0.0010039895], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0010039895, -# "1": -6.907254, -# "2": -13.743192, -# "false": -15.227567, -# "3": -15.297879, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 14, -# "logprobs": { -# "text_offset": [106], -# "token_logprobs": [-0.0005681643], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0005681643, -# "1": -7.5005684, -# "__": -11.836506, -# "zero": -13.242756, -# "file": -13.445881, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 15, -# "logprobs": { -# "text_offset": [146], -# "token_logprobs": [-3.9769227e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -3.9769227e-5, -# "1": -10.15629, -# "000": -15.078165, -# "00": -15.664103, -# "zero": -16.015665, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 16, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0006509595], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0006509595, -# "1": -7.344401, -# "2": -13.352214, -# " ": -13.852214, -# "3": -14.680339, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 17, -# "logprobs": { -# "text_offset": [103], -# "token_logprobs": [-0.0093299495], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0093299495, -# "1": -4.681205, -# "2": -11.173392, -# "3": -13.439017, -# "00": -14.673392, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 18, -# "logprobs": { -# "text_offset": [130], -# "token_logprobs": [-0.00024382756], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00024382756, -# "1": -8.328369, -# " ": -13.640869, -# "zero": -14.859619, -# "null": -16.51587, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 19, -# "logprobs": { -# "text_offset": [107], -# "token_logprobs": [-0.0006452414], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0006452414, -# "1": -7.36002, -# "00": -12.328771, -# "000": -12.961583, -# "2": -14.211583, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 20, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0012751155], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0012751155, -# "1": -6.67315, -# "__": -11.970025, -# "<|endoftext|>": -14.907525, -# "3": -14.930963, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 21, -# "logprobs": { -# "text_offset": [107], -# "token_logprobs": [-7.1954215e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -7.1954215e-5, -# "1": -9.640697, -# "00": -13.500072, -# "000": -13.523509, -# "__": -13.945384, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 22, -# "logprobs": { -# "text_offset": [108], -# "token_logprobs": [-0.0032367748], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0032367748, -# "1": -5.737612, -# "<|endoftext|>": -13.940737, -# "2": -14.167299, -# "00": -14.292299, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 23, -# "logprobs": { -# "text_offset": [117], -# "token_logprobs": [-0.00018673266], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00018673266, -# "1": -8.593937, -# "zero": -15.179874, -# "null": -15.515812, -# "None": -15.851749, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 24, -# "logprobs": { -# "text_offset": [104], -# "token_logprobs": [-0.0010223285], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0010223285, -# "1": -6.8916473, -# "__": -13.05571, -# "00": -14.071335, -# "zero": -14.235397, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 25, -# "logprobs": { -# "text_offset": [108], -# "token_logprobs": [-0.0038979414], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0038979414, -# "1": -5.550773, -# "2": -13.160148, -# "00": -14.144523, -# "3": -14.41796, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 26, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.00074721366], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00074721366, -# "1": -7.219497, -# "3": -11.430435, -# "2": -13.367935, -# " ": -13.735123, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 27, -# "logprobs": { -# "text_offset": [146], -# "token_logprobs": [-8.566264e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -8.566264e-5, -# "1": -9.375086, -# "000": -15.359461, -# "__": -15.671961, -# "00": -15.679773, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 28, -# "logprobs": { -# "text_offset": [119], -# "token_logprobs": [-0.000274683], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.000274683, -# "1": -8.2034, -# "00": -14.898712, -# "2": -15.633087, -# "__": -16.844025, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 29, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.014869375], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.014869375, -# "1": -4.217994, -# "2": -11.63987, -# "3": -11.944557, -# "5": -12.26487, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 30, -# "logprobs": { -# "text_offset": [110], -# "token_logprobs": [-0.010907865], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.010907865, -# "1": -4.5265326, -# "2": -11.440596, -# "<|endoftext|>": -12.456221, -# "file": -13.049971, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 31, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.00070528337], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00070528337, -# "1": -7.2663302, -# "6": -13.141331, -# "2": -13.797581, -# "3": -13.836643, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 32, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0004983439], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0004983439, -# "1": -7.6098733, -# "3": -14.211436, -# "2": -14.336436, -# " ": -15.117686, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 33, -# "logprobs": { -# "text_offset": [110], -# "token_logprobs": [-3.6908343e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -3.6908343e-5, -# "1": -10.250037, -# "00": -14.2266, -# "__": -14.7266, -# "000": -16.164099, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 34, -# "logprobs": { -# "text_offset": [104], -# "token_logprobs": [-0.003917157], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.003917157, -# "1": -5.550792, -# "2": -11.355479, -# "00": -12.777354, -# "3": -13.652354, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 35, -# "logprobs": { -# "text_offset": [146], -# "token_logprobs": [-5.0139948e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -5.0139948e-5, -# "1": -9.921926, -# "000": -14.851613, -# "00": -15.414113, -# "zero": -15.687551, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 36, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0005143099], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0005143099, -# "1": -7.5786395, -# " ": -14.406764, -# "00": -14.570827, -# "999": -14.633327, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 37, -# "logprobs": { -# "text_offset": [103], -# "token_logprobs": [-0.00013691289], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00013691289, -# "1": -8.968887, -# "__": -12.547012, -# "zero": -13.57045, -# "00": -13.8517, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 38, -# "logprobs": { -# "text_offset": [103], -# "token_logprobs": [-0.00032569113], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00032569113, -# "1": -8.047201, -# "2": -13.570639, -# "zero": -14.023764, -# "false": -14.726889, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 39, -# "logprobs": { -# "text_offset": [113], -# "token_logprobs": [-3.7146747e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -3.7146747e-5, -# "1": -10.203162, -# "zero": -18.437536, -# "2": -20.117224, -# " zero": -20.210974, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 40, -# "logprobs": { -# "text_offset": [110], -# "token_logprobs": [-7.4695905e-5], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -7.4695905e-5, -# "1": -9.515699, -# "00": -14.836012, -# "__": -16.093824, -# "file": -16.468824, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 41, -# "logprobs": { -# "text_offset": [111], -# "token_logprobs": [-0.02289473], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.02289473, -# "1": -3.7885196, -# "2": -12.499457, -# "3": -14.546332, -# "00": -15.66352, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 42, -# "logprobs": { -# "text_offset": [108], -# "token_logprobs": [-0.0011367622], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0011367622, -# "1": -6.782387, -# "2": -13.493324, -# "00": -15.071449, -# "zero": -15.727699, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 43, -# "logprobs": { -# "text_offset": [115], -# "token_logprobs": [-0.0006384541], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0006384541, -# "1": -7.3600135, -# "00": -14.0397005, -# "2": -14.4303255, -# "000": -15.563138, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 44, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0007382771], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0007382771, -# "1": -7.219488, -# "4": -13.516363, -# "2": -13.555426, -# "3": -13.602301, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 45, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0014242834], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0014242834, -# "1": -6.5639243, -# "2": -12.493611, -# "__": -12.712361, -# "3": -12.884236, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 46, -# "logprobs": { -# "text_offset": [111], -# "token_logprobs": [-0.00017088225], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00017088225, -# "1": -8.765796, -# "zero": -12.695483, -# "__": -12.804858, -# "time": -12.882983, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 47, -# "logprobs": { -# "text_offset": [146], -# "token_logprobs": [-0.000107238506], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.000107238506, -# "1": -9.171982, -# "000": -13.648544, -# "__": -14.531357, -# "zero": -14.586044, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 48, -# "logprobs": { -# "text_offset": [106], -# "token_logprobs": [-0.0028172398], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0028172398, -# "1": -5.877817, -# "00": -12.16688, -# "2": -12.487192, -# "000": -14.182505, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 49, -# "logprobs": { -# "text_offset": [104], -# "token_logprobs": [-0.00043460296], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.00043460296, -# "1": -7.7816844, -# "00": -13.570747, -# "2": -13.60981, -# "__": -13.789497, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 50, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0046973573], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0046973573, -# "1": -5.3640723, -# "null": -14.082823, -# " ": -14.707823, -# "2": -14.746885, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 51, -# "logprobs": { -# "text_offset": [100], -# "token_logprobs": [-0.2487161], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.2487161, -# "1": -1.5143411, -# "2": -9.037779, -# "3": -10.100279, -# "4": -10.756529, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 52, -# "logprobs": { -# "text_offset": [108], -# "token_logprobs": [-0.0011751055], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0011751055, -# "1": -6.751175, -# " ": -13.73555, -# "2": -15.258987, -# "3": -15.399612, -# } -# ], -# }, -# "text": "0", -# }, -# { -# "finish_reason": "length", -# "index": 53, -# "logprobs": { -# "text_offset": [143], -# "token_logprobs": [-0.0012339224], -# "tokens": ["0"], -# "top_logprobs": [ -# { -# "0": -0.0012339224, -# "1": -6.719984, -# "6": -11.430922, -# "3": -12.165297, -# "2": -12.696547, -# } -# ], -# }, -# "text": "0", -# }, -# ], -# "created": 1712163061, -# "model": "ft:babbage-002:ai-r-d-zapai:v3-fields-used:84jb9rtr", -# "object": "text_completion", -# "system_fingerprint": None, -# "usage": {"completion_tokens": 54, "prompt_tokens": 1877, "total_tokens": 1931}, -# } - -# text_completion_obj = TextCompletionResponse(**openai_object) - -# ## WRITE UNIT TESTS FOR TEXT_COMPLETION_OBJECT -# assert text_completion_obj.id == "cmpl-99y7B2svVoRWe1xd7UFRmeGjZrFSh" -# assert text_completion_obj.object == "text_completion" -# assert text_completion_obj.created == 1712163061 -# assert ( -# text_completion_obj.model -# == "ft:babbage-002:ai-r-d-zapai:v3-fields-used:84jb9rtr" -# ) -# assert text_completion_obj.system_fingerprint == None -# assert len(text_completion_obj.choices) == len(openai_object["choices"]) - -# # TEST FIRST CHOICE # -# first_text_completion_obj = text_completion_obj.choices[0] -# assert first_text_completion_obj.index == 0 -# assert first_text_completion_obj.logprobs.text_offset == [101] -# assert first_text_completion_obj.logprobs.tokens == ["0"] -# assert first_text_completion_obj.logprobs.token_logprobs == [-0.00023488728] -# assert len(first_text_completion_obj.logprobs.top_logprobs) == len( -# openai_object["choices"][0]["logprobs"]["top_logprobs"] -# ) -# assert first_text_completion_obj.text == "0" -# assert first_text_completion_obj.finish_reason == "length" - -# # TEST SECOND CHOICE # -# second_text_completion_obj = text_completion_obj.choices[1] -# assert second_text_completion_obj.index == 1 -# assert second_text_completion_obj.logprobs.text_offset == [116] -# assert second_text_completion_obj.logprobs.tokens == ["0"] -# assert second_text_completion_obj.logprobs.token_logprobs == [-0.013745008] -# assert len(second_text_completion_obj.logprobs.top_logprobs) == len( -# openai_object["choices"][0]["logprobs"]["top_logprobs"] -# ) -# assert second_text_completion_obj.text == "0" -# assert second_text_completion_obj.finish_reason == "length" - -# # TEST LAST CHOICE # -# last_text_completion_obj = text_completion_obj.choices[-1] -# assert last_text_completion_obj.index == 53 -# assert last_text_completion_obj.logprobs.text_offset == [143] -# assert last_text_completion_obj.logprobs.tokens == ["0"] -# assert last_text_completion_obj.logprobs.token_logprobs == [-0.0012339224] -# assert len(last_text_completion_obj.logprobs.top_logprobs) == len( -# openai_object["choices"][0]["logprobs"]["top_logprobs"] -# ) -# assert last_text_completion_obj.text == "0" -# assert last_text_completion_obj.finish_reason == "length" - -# assert text_completion_obj.usage.completion_tokens == 54 -# assert text_completion_obj.usage.prompt_tokens == 1877 -# assert text_completion_obj.usage.total_tokens == 1931 - - -# def test_completion_openai_prompt(): -# try: -# print("\n text 003 test\n") -# response = text_completion( -# model="gpt-3.5-turbo-instruct", -# prompt=["What's the weather in SF?", "How is Manchester?"], -# ) -# print(response) -# assert len(response.choices) == 2 -# response_str = response["choices"][0]["text"] -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_openai_prompt() - - -# def test_completion_openai_engine_and_model(): -# try: -# print("\n text 003 test\n") -# litellm.set_verbose = True -# response = text_completion( -# model="gpt-3.5-turbo-instruct", -# engine="anything", -# prompt="What's the weather in SF?", -# max_tokens=5, -# ) -# print(response) -# response_str = response["choices"][0]["text"] -# # print(response.choices[0]) -# # print(response.choices[0].text) -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_openai_engine_and_model() - - -# def test_completion_openai_engine(): -# try: -# print("\n text 003 test\n") -# litellm.set_verbose = True -# response = text_completion( -# engine="gpt-3.5-turbo-instruct", -# prompt="What's the weather in SF?", -# max_tokens=5, -# ) -# print(response) -# response_str = response["choices"][0]["text"] -# # print(response.choices[0]) -# # print(response.choices[0].text) -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_openai_engine() - - -# def test_completion_chatgpt_prompt(): -# try: -# print("\n gpt3.5 test\n") -# response = text_completion( -# model="gpt-3.5-turbo", prompt="What's the weather in SF?" -# ) -# print(response) -# response_str = response["choices"][0]["text"] -# print("\n", response.choices) -# print("\n", response.choices[0]) -# # print(response.choices[0].text) -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_chatgpt_prompt() - - -# def test_text_completion_basic(): -# try: -# print("\n test 003 with logprobs \n") -# litellm.set_verbose = False -# response = text_completion( -# model="gpt-3.5-turbo-instruct", -# prompt="good morning", -# max_tokens=10, -# logprobs=10, -# ) -# print(response) -# print(response.choices) -# print(response.choices[0]) -# # print(response.choices[0].text) -# response_str = response["choices"][0]["text"] -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_text_completion_basic() - - -# def test_completion_text_003_prompt_array(): -# try: -# litellm.set_verbose = False -# response = text_completion( -# model="gpt-3.5-turbo-instruct", -# prompt=token_prompt, # token prompt is a 2d list -# ) -# print("\n\n response") - -# print(response) -# # response_str = response["choices"][0]["text"] -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_text_003_prompt_array() - - -# # not including this in our ci cd pipeline, since we don't want to fail tests due to an unstable replit -# # def test_text_completion_with_proxy(): -# # try: -# # litellm.set_verbose=True -# # response = text_completion( -# # model="facebook/opt-125m", -# # prompt='Write a tagline for a traditional bavarian tavern', -# # api_base="https://openai-proxy.berriai.repl.co/v1", -# # custom_llm_provider="openai", -# # temperature=0, -# # max_tokens=10, -# # ) -# # print("\n\n response") - -# # print(response) -# # except Exception as e: -# # pytest.fail(f"Error occurred: {e}") -# # test_text_completion_with_proxy() - - -# ##### hugging face tests -# def test_completion_hf_prompt_array(): -# try: -# litellm.set_verbose = True -# print("\n testing hf mistral\n") -# response = text_completion( -# model="huggingface/mistralai/Mistral-7B-v0.1", -# prompt=token_prompt, # token prompt is a 2d list, -# max_tokens=0, -# temperature=0.0, -# # echo=True, # hugging face inference api is currently raising errors for this, looks like they have a regression on their side -# ) -# print("\n\n response") - -# print(response) -# print(response.choices) -# assert len(response.choices) == 2 -# # response_str = response["choices"][0]["text"] -# except Exception as e: -# print(str(e)) -# if "is currently loading" in str(e): -# return -# if "Service Unavailable" in str(e): -# return -# pytest.fail(f"Error occurred: {e}") - - -# # test_completion_hf_prompt_array() - - -# def test_text_completion_stream(): -# try: -# response = text_completion( -# model="huggingface/mistralai/Mistral-7B-v0.1", -# prompt="good morning", -# stream=True, -# max_tokens=10, -# ) -# for chunk in response: -# print(f"chunk: {chunk}") -# except Exception as e: -# pytest.fail(f"GOT exception for HF In streaming{e}") - - -# # test_text_completion_stream() - -# # async def test_text_completion_async_stream(): -# # try: -# # response = await atext_completion( -# # model="text-completion-openai/gpt-3.5-turbo-instruct", -# # prompt="good morning", -# # stream=True, -# # max_tokens=10, -# # ) -# # async for chunk in response: -# # print(f"chunk: {chunk}") -# # except Exception as e: -# # pytest.fail(f"GOT exception for HF In streaming{e}") - -# # asyncio.run(test_text_completion_async_stream()) - - -# def test_async_text_completion(): -# litellm.set_verbose = True -# print("test_async_text_completion") - -# async def test_get_response(): -# try: -# response = await litellm.atext_completion( -# model="gpt-3.5-turbo-instruct", -# prompt="good morning", -# stream=False, -# max_tokens=10, -# ) -# print(f"response: {response}") -# except litellm.Timeout as e: -# print(e) -# except Exception as e: -# print(e) - -# asyncio.run(test_get_response()) - - -# @pytest.mark.skip(reason="Skip flaky tgai test") -# def test_async_text_completion_together_ai(): -# litellm.set_verbose = True -# print("test_async_text_completion") - -# async def test_get_response(): -# try: -# response = await litellm.atext_completion( -# model="together_ai/mistralai/Mixtral-8x7B-Instruct-v0.1", -# prompt="good morning", -# max_tokens=10, -# ) -# print(f"response: {response}") -# except litellm.Timeout as e: -# print(e) -# except Exception as e: -# pytest.fail("An unexpected error occurred") - -# asyncio.run(test_get_response()) - - -# # test_async_text_completion() - - -# def test_async_text_completion_stream(): -# # tests atext_completion + streaming - assert only one finish reason sent -# litellm.set_verbose = False -# print("test_async_text_completion with stream") - -# async def test_get_response(): -# try: -# response = await litellm.atext_completion( -# model="gpt-3.5-turbo-instruct", -# prompt="good morning", -# stream=True, -# ) -# print(f"response: {response}") - -# num_finish_reason = 0 -# async for chunk in response: -# print(chunk) -# if chunk["choices"][0].get("finish_reason") is not None: -# num_finish_reason += 1 -# print("finish_reason", chunk["choices"][0].get("finish_reason")) - -# assert ( -# num_finish_reason == 1 -# ), f"expected only one finish reason. Got {num_finish_reason}" -# except Exception as e: -# pytest.fail(f"GOT exception for gpt-3.5 instruct In streaming{e}") - -# asyncio.run(test_get_response()) - - -# # test_async_text_completion_stream() - - -# @pytest.mark.asyncio -# async def test_async_text_completion_chat_model_stream(): -# try: -# response = await litellm.atext_completion( -# model="gpt-3.5-turbo", -# prompt="good morning", -# stream=True, -# max_tokens=10, -# ) - -# num_finish_reason = 0 -# chunks = [] -# async for chunk in response: -# print(chunk) -# chunks.append(chunk) -# if chunk["choices"][0].get("finish_reason") is not None: -# num_finish_reason += 1 - -# assert ( -# num_finish_reason == 1 -# ), f"expected only one finish reason. Got {num_finish_reason}" -# response_obj = litellm.stream_chunk_builder(chunks=chunks) -# cost = litellm.completion_cost(completion_response=response_obj) -# assert cost > 0 -# except Exception as e: -# pytest.fail(f"GOT exception for gpt-3.5 In streaming{e}") - - -# # asyncio.run(test_async_text_completion_chat_model_stream()) - - -# @pytest.mark.asyncio -# async def test_completion_codestral_fim_api(): -# try: -# litellm.set_verbose = True -# import logging - -# from litellm._logging import verbose_logger - -# verbose_logger.setLevel(level=logging.DEBUG) -# response = await litellm.atext_completion( -# model="text-completion-codestral/codestral-2405", -# prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():", -# suffix="return True", -# temperature=0, -# top_p=1, -# max_tokens=10, -# min_tokens=10, -# seed=10, -# stop=["return"], -# ) -# # Add any assertions here to check the response -# print(response) - -# assert response.choices[0].text is not None -# assert len(response.choices[0].text) > 0 - -# # cost = litellm.completion_cost(completion_response=response) -# # print("cost to make mistral completion=", cost) -# # assert cost > 0.0 -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# @pytest.mark.asyncio -# async def test_completion_codestral_fim_api_stream(): -# try: -# import logging - -# from litellm._logging import verbose_logger - -# litellm.set_verbose = False - -# # verbose_logger.setLevel(level=logging.DEBUG) -# response = await litellm.atext_completion( -# model="text-completion-codestral/codestral-2405", -# prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():", -# suffix="return True", -# temperature=0, -# top_p=1, -# stream=True, -# seed=10, -# stop=["return"], -# ) - -# full_response = "" -# # Add any assertions here to check the response -# async for chunk in response: -# print(chunk) -# full_response += chunk.get("choices")[0].get("text") or "" - -# print("full_response", full_response) - -# assert len(full_response) > 2 # we at least have a few chars in response :) - -# # cost = litellm.completion_cost(completion_response=response) -# # print("cost to make mistral completion=", cost) -# # assert cost > 0.0 -# except Exception as e: -# pytest.fail(f"Error occurred: {e}") - - -# def mock_post(*args, **kwargs): -# mock_response = MagicMock() -# mock_response.status_code = 200 -# mock_response.headers = {"Content-Type": "application/json"} -# mock_response.model_dump.return_value = { -# "id": "cmpl-7a59383dd4234092b9e5d652a7ab8143", -# "object": "text_completion", -# "created": 1718824735, -# "model": "Sao10K/L3-70B-Euryale-v2.1", -# "choices": [ -# { -# "index": 0, -# "text": ") might be faster than then answering, and the added time it takes for the", -# "logprobs": None, -# "finish_reason": "length", -# "stop_reason": None, -# } -# ], -# "usage": {"prompt_tokens": 2, "total_tokens": 18, "completion_tokens": 16}, -# } -# return mock_response - - -# def test_completion_vllm(): -# """ -# Asserts a text completion call for vllm actually goes to the text completion endpoint -# """ -# from openai import OpenAI - -# client = OpenAI(api_key="my-fake-key") - -# with patch.object(client.completions, "create", side_effect=mock_post) as mock_call: -# response = text_completion( -# model="openai/gemini-1.5-flash", prompt="ping", client=client, hello="world" -# ) -# print(response) - -# assert response.usage.prompt_tokens == 2 - -# mock_call.assert_called_once() - -# assert "hello" in mock_call.call_args.kwargs["extra_body"] + print(response) diff --git a/litellm/types/adapter.py b/litellm/types/adapter.py new file mode 100644 index 00000000000..2995cfbc1c2 --- /dev/null +++ b/litellm/types/adapter.py @@ -0,0 +1,10 @@ +from typing import List + +from typing_extensions import Dict, Required, TypedDict, override + +from litellm.integrations.custom_logger import CustomLogger + + +class AdapterItem(TypedDict): + id: str + adapter: CustomLogger diff --git a/litellm/types/llms/anthropic.py b/litellm/types/llms/anthropic.py index 8d8280ea794..7df73377d03 100644 --- a/litellm/types/llms/anthropic.py +++ b/litellm/types/llms/anthropic.py @@ -9,25 +9,27 @@ class AnthropicMessagesToolChoice(TypedDict, total=False): name: str -class AnthopicMessagesAssistantMessageTextContentParam(TypedDict, total=False): - type: Required[Literal["text"]] +class AnthropicMessagesTool(TypedDict, total=False): + name: Required[str] + description: str + input_schema: Required[dict] + +class AnthropicMessagesTextParam(TypedDict): + type: Literal["text"] text: str -class AnthopicMessagesAssistantMessageToolCallParam(TypedDict, total=False): - type: Required[Literal["tool_use"]] - +class AnthropicMessagesToolUseParam(TypedDict): + type: Literal["tool_use"] id: str - name: str - input: dict AnthropicMessagesAssistantMessageValues = Union[ - AnthopicMessagesAssistantMessageTextContentParam, - AnthopicMessagesAssistantMessageToolCallParam, + AnthropicMessagesTextParam, + AnthropicMessagesToolUseParam, ] @@ -46,6 +48,72 @@ class AnthopicMessagesAssistantMessageParam(TypedDict, total=False): """ +class AnthropicImageParamSource(TypedDict): + type: Literal["base64"] + media_type: str + data: str + + +class AnthropicMessagesImageParam(TypedDict): + type: Literal["image"] + source: AnthropicImageParamSource + + +class AnthropicMessagesToolResultContent(TypedDict): + type: Literal["text"] + text: str + + +class AnthropicMessagesToolResultParam(TypedDict, total=False): + type: Required[Literal["tool_result"]] + tool_use_id: Required[str] + is_error: bool + content: Union[ + str, + Iterable[ + Union[AnthropicMessagesToolResultContent, AnthropicMessagesImageParam] + ], + ] + + +AnthropicMessagesUserMessageValues = Union[ + AnthropicMessagesTextParam, + AnthropicMessagesImageParam, + AnthropicMessagesToolResultParam, +] + + +class AnthropicMessagesUserMessageParam(TypedDict, total=False): + role: Required[Literal["user"]] + content: Required[Union[str, Iterable[AnthropicMessagesUserMessageValues]]] + + +class AnthropicMetadata(TypedDict, total=False): + user_id: str + + +class AnthropicMessagesRequest(TypedDict, total=False): + model: Required[str] + messages: Required[ + List[ + Union[ + AnthropicMessagesUserMessageParam, + AnthopicMessagesAssistantMessageParam, + ] + ] + ] + max_tokens: Required[int] + metadata: AnthropicMetadata + stop_sequences: List[str] + stream: bool + system: str + temperature: float + tool_choice: AnthropicMessagesToolChoice + tools: List[AnthropicMessagesTool] + top_k: int + top_p: float + + class ContentTextBlockDelta(TypedDict): """ 'delta': {'type': 'text_delta', 'text': 'Hello'} diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 6fc0593b981..63f07f2ca1a 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -305,7 +305,13 @@ class ChatCompletionToolCallFunctionChunk(TypedDict, total=False): arguments: str -class ChatCompletionToolCallChunk(TypedDict): +class ChatCompletionAssistantToolCall(TypedDict): + id: Optional[str] + type: Literal["function"] + function: ChatCompletionToolCallFunctionChunk + + +class ChatCompletionToolCallChunk(TypedDict): # result of /chat/completions call id: Optional[str] type: Literal["function"] function: ChatCompletionToolCallFunctionChunk @@ -319,6 +325,107 @@ class ChatCompletionDeltaToolCallChunk(TypedDict, total=False): index: int +class ChatCompletionTextObject(TypedDict): + type: Literal["text"] + text: str + + +class ChatCompletionImageUrlObject(TypedDict, total=False): + url: Required[str] + detail: str + + +class ChatCompletionImageObject(TypedDict): + type: Literal["image_url"] + image_url: ChatCompletionImageUrlObject + + +class ChatCompletionUserMessage(TypedDict): + role: Literal["user"] + content: Union[ + str, Iterable[Union[ChatCompletionTextObject, ChatCompletionImageObject]] + ] + + +class ChatCompletionAssistantMessage(TypedDict, total=False): + role: Required[Literal["assistant"]] + content: Optional[str] + name: str + tool_calls: List[ChatCompletionAssistantToolCall] + + +class ChatCompletionToolMessage(TypedDict): + role: Literal["tool"] + content: str + tool_call_id: str + + +class ChatCompletionSystemMessage(TypedDict, total=False): + role: Required[Literal["system"]] + content: Required[str] + name: str + + +AllMessageValues = Union[ + ChatCompletionUserMessage, + ChatCompletionAssistantMessage, + ChatCompletionToolMessage, + ChatCompletionSystemMessage, +] + + +class ChatCompletionToolChoiceFunctionParam(TypedDict): + name: str + + +class ChatCompletionToolChoiceObjectParam(TypedDict): + type: Literal["function"] + function: ChatCompletionToolChoiceFunctionParam + + +ChatCompletionToolChoiceStringValues = Literal["none", "auto", "required"] + +ChatCompletionToolChoiceValues = Union[ + ChatCompletionToolChoiceStringValues, ChatCompletionToolChoiceObjectParam +] + + +class ChatCompletionToolParamFunctionChunk(TypedDict, total=False): + name: Required[str] + description: str + parameters: dict + + +class ChatCompletionToolParam(TypedDict): + type: Literal["function"] + function: ChatCompletionToolParamFunctionChunk + + +class ChatCompletionRequest(TypedDict, total=False): + model: Required[str] + messages: Required[List[AllMessageValues]] + frequency_penalty: float + logit_bias: dict + logprobs: bool + top_logprobs: int + max_tokens: int + n: int + presence_penalty: float + response_format: dict + seed: int + service_tier: str + stop: Union[str, List[str]] + stream_options: dict + temperature: float + top_p: float + tools: List[ChatCompletionToolParam] + tool_choice: ChatCompletionToolChoiceValues + parallel_tool_calls: bool + function_call: Union[str, dict] + functions: List + user: str + + class ChatCompletionDeltaChunk(TypedDict, total=False): content: Optional[str] tool_calls: List[ChatCompletionDeltaToolCallChunk] From bac795218d33f2be66f3a22b319736d312caedf4 Mon Sep 17 00:00:00 2001 From: Fabian Reinold <32450519+freinold@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:26:42 +0200 Subject: [PATCH 05/89] Add prisma binary_cache_dir specification to pyproject.toml --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 8bac85210da..1f5d17df6b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,3 +98,7 @@ version_files = [ [tool.mypy] plugins = "pydantic.mypy" + +[tool.prisma] +# cache engine binaries in a directory relative to your project +binary_cache_dir = '.binaries' From 3de1743cd85cb49b195cfd977f5f6df9c4ea33a0 Mon Sep 17 00:00:00 2001 From: Fabian Reinold <32450519+freinold@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:27:01 +0200 Subject: [PATCH 06/89] Change prisma configuration to capsulate all binaries inside application directory with no connection to active user --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1f5d17df6b0..7ce3182fe70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,4 +101,6 @@ plugins = "pydantic.mypy" [tool.prisma] # cache engine binaries in a directory relative to your project -binary_cache_dir = '.binaries' +# binary_cache_dir = '.binaries' +home_dir = '.prisma' +nodeenv_cache_dir = '.nodeenv' From e20b540dac8547b73815aca2dec825df550943c2 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 10:10:32 -0700 Subject: [PATCH 07/89] add validation on slack --- litellm/integrations/slack_alerting.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/litellm/integrations/slack_alerting.py b/litellm/integrations/slack_alerting.py index 04195705a06..437e8ce135f 100644 --- a/litellm/integrations/slack_alerting.py +++ b/litellm/integrations/slack_alerting.py @@ -1538,7 +1538,11 @@ Model Info: final_value = response_s total_tokens = 0 - if isinstance(response_obj, litellm.ModelResponse): + if isinstance(response_obj, litellm.ModelResponse) and ( + hasattr(response_obj, "usage") + and response_obj.usage is not None + and hasattr(response_obj.usage, "completion_tokens") + ): completion_tokens = response_obj.usage.completion_tokens if completion_tokens is not None and completion_tokens > 0: final_value = float( @@ -1557,8 +1561,7 @@ Model Info: ) except Exception as e: verbose_proxy_logger.error( - "[Non-Blocking Error] Slack Alerting: Got error in logging LLM deployment latency: ", - e, + f"[Non-Blocking Error] Slack Alerting: Got error in logging LLM deployment latency: {str(e)}" ) pass From 99fd3889431dc82ba39f279952c406b05548df10 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 14:51:48 -0700 Subject: [PATCH 08/89] add retrive file to litellm SDK --- litellm/__init__.py | 3 +- litellm/files/main.py | 150 +++++++++++++++++++++++++++++++++++++++++ litellm/llms/openai.py | 46 +++++++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 litellm/files/main.py diff --git a/litellm/__init__.py b/litellm/__init__.py index f0dab5e29ee..f9e7106090b 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -364,7 +364,7 @@ for key, value in model_cost.items(): elif value.get("litellm_provider") == "mistral": mistral_chat_models.append(key) elif value.get("litellm_provider") == "anthropic": - anthropic_models.append(key) + anthropic_models.append(key) elif value.get("litellm_provider") == "empower": empower_models.append(key) elif value.get("litellm_provider") == "openrouter": @@ -879,5 +879,6 @@ from .proxy.proxy_cli import run_server from .router import Router from .assistants.main import * from .batches.main import * +from .files.main import * from .scheduler import * from .cost_calculator import response_cost_calculator, cost_per_token diff --git a/litellm/files/main.py b/litellm/files/main.py new file mode 100644 index 00000000000..e11833eb446 --- /dev/null +++ b/litellm/files/main.py @@ -0,0 +1,150 @@ +""" +Main File for Files API implementation + +https://platform.openai.com/docs/api-reference/files + +""" + +import asyncio +import contextvars +import os +from functools import partial +from typing import Any, Coroutine, Dict, Literal, Optional, Union + +import httpx +from openai.types.file_object import FileObject + +import litellm +from litellm import client +from litellm.batches.main import openai_files_instance +from litellm.types.llms.openai import ( + Batch, + CreateFileRequest, + FileContentRequest, + FileTypes, + HttpxBinaryResponseContent, +) +from litellm.types.router import * +from litellm.utils import supports_httpx_timeout + + +async def afile_retrieve( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Coroutine[Any, Any, FileObject]: + """ + Async: Get file contents + + LiteLLM Equivalent of GET https://api.openai.com/v1/files + """ + try: + loop = asyncio.get_event_loop() + kwargs["is_async"] = True + + # Use a partial function to pass your keyword arguments + func = partial( + file_retrieve, + file_id, + custom_llm_provider, + extra_headers, + extra_body, + **kwargs, + ) + + # Add the context to the function + ctx = contextvars.copy_context() + func_with_context = partial(ctx.run, func) + init_response = await loop.run_in_executor(None, func_with_context) + if asyncio.iscoroutine(init_response): + response = await init_response + else: + response = init_response # type: ignore + + return response + except Exception as e: + raise e + + +def file_retrieve( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> FileObject: + """ + Returns the contents of the specified file. + + LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files + """ + try: + optional_params = GenericLiteLLMParams(**kwargs) + if custom_llm_provider == "openai": + # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there + api_base = ( + optional_params.api_base + or litellm.api_base + or os.getenv("OPENAI_API_BASE") + or "https://api.openai.com/v1" + ) + organization = ( + optional_params.organization + or litellm.organization + or os.getenv("OPENAI_ORGANIZATION", None) + or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 + ) + # set API KEY + api_key = ( + optional_params.api_key + or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there + or litellm.openai_key + or os.getenv("OPENAI_API_KEY") + ) + ### TIMEOUT LOGIC ### + timeout = ( + optional_params.timeout or kwargs.get("request_timeout", 600) or 600 + ) + # set timeout for 10 minutes by default + + if ( + timeout is not None + and isinstance(timeout, httpx.Timeout) + and supports_httpx_timeout(custom_llm_provider) == False + ): + read_timeout = timeout.read or 600 + timeout = read_timeout # default 10 min timeout + elif timeout is not None and not isinstance(timeout, httpx.Timeout): + timeout = float(timeout) # type: ignore + elif timeout is None: + timeout = 600.0 + + _is_async = kwargs.pop("is_async", False) is True + + response = openai_files_instance.retrieve_file( + file_id=file_id, + _is_async=_is_async, + api_base=api_base, + api_key=api_key, + timeout=timeout, + max_retries=optional_params.max_retries, + organization=organization, + ) + else: + raise litellm.exceptions.BadRequestError( + message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( + custom_llm_provider + ), + model="n/a", + llm_provider=custom_llm_provider, + response=httpx.Response( + status_code=400, + content="Unsupported provider", + request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore + ), + ) + return response + except Exception as e: + raise e diff --git a/litellm/llms/openai.py b/litellm/llms/openai.py index e6056691e73..17f4f5159b4 100644 --- a/litellm/llms/openai.py +++ b/litellm/llms/openai.py @@ -2064,6 +2064,52 @@ class OpenAIFilesAPI(BaseLLM): return response + async def aretrieve_file( + self, + file_id: str, + openai_client: AsyncOpenAI, + ) -> FileObject: + response = await openai_client.files.retrieve(file_id=file_id) + return response + + def retrieve_file( + self, + _is_async: bool, + file_id: str, + api_base: str, + api_key: Optional[str], + timeout: Union[float, httpx.Timeout], + max_retries: Optional[int], + organization: Optional[str], + client: Optional[Union[OpenAI, AsyncOpenAI]] = None, + ): + openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client( + api_key=api_key, + api_base=api_base, + timeout=timeout, + max_retries=max_retries, + organization=organization, + client=client, + _is_async=_is_async, + ) + if openai_client is None: + raise ValueError( + "OpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment." + ) + + if _is_async is True: + if not isinstance(openai_client, AsyncOpenAI): + raise ValueError( + "OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client." + ) + return self.aretrieve_file( # type: ignore + file_id=file_id, + openai_client=openai_client, + ) + response = openai_client.files.retrieve(file_id=file_id) + + return response + class OpenAIBatchesAPI(BaseLLM): """ From 393ce7df141fbcd6bca87a3da9979f1c7d75180b Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 14:55:10 -0700 Subject: [PATCH 09/89] add /files endpoints --- .../openai_files_endpoints/files_endpoints.py | 265 ++++++++++++++++++ litellm/proxy/proxy_server.py | 116 +------- 2 files changed, 269 insertions(+), 112 deletions(-) create mode 100644 litellm/proxy/openai_files_endpoints/files_endpoints.py diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py new file mode 100644 index 00000000000..d8f0a7e287e --- /dev/null +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -0,0 +1,265 @@ +###################################################################### + +# /v1/files Endpoints + +# Equivalent of https://platform.openai.com/docs/api-reference/files +###################################################################### + +import asyncio +import traceback +from datetime import datetime, timedelta, timezone +from typing import List, Optional + +import fastapi +import httpx +from fastapi import ( + APIRouter, + Depends, + File, + Form, + Header, + HTTPException, + Request, + Response, + UploadFile, + status, +) + +import litellm +from litellm import CreateFileRequest, FileContentRequest +from litellm._logging import verbose_proxy_logger +from litellm.batches.main import FileObject +from litellm.proxy._types import * +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + +router = APIRouter() + + +@router.post( + "/v1/files", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +@router.post( + "/files", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +async def create_file( + request: Request, + fastapi_response: Response, + purpose: str = Form(...), + file: UploadFile = File(...), + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Upload a file that can be used across - Assistants API, Batch API + This is the equivalent of POST https://api.openai.com/v1/files + + Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/create + + Example Curl + ``` + curl http://localhost:4000/v1/files \ + -H "Authorization: Bearer sk-1234" \ + -F purpose="batch" \ + -F file="@mydata.jsonl" + + ``` + """ + from litellm.proxy.proxy_server import ( + add_litellm_data_to_request, + general_settings, + get_custom_headers, + proxy_config, + proxy_logging_obj, + version, + ) + + data: Dict = {} + try: + # Use orjson to parse JSON data, orjson speeds up requests significantly + # Read the file content + file_content = await file.read() + # Prepare the data for forwarding + + data = {"purpose": purpose} + + # Include original request and headers in the data + data = await add_litellm_data_to_request( + data=data, + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # Prepare the file data according to FileTypes + file_data = (file.filename, file_content, file.content_type) + + _create_file_request = CreateFileRequest(file=file_data, **data) + + # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch + response = await litellm.acreate_file( + custom_llm_provider="openai", **_create_file_request + ) + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" + ) + ) + + ### RESPONSE HEADERS ### + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + model_region=getattr(user_api_key_dict, "allowed_model_region", ""), + ) + ) + + return response + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.create_file(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + if isinstance(e, HTTPException): + raise ProxyException( + message=getattr(e, "message", str(e.detail)), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + ) + else: + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) + + +@router.get( + "/v1/files/{file_id:path}", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +@router.get( + "/files/{file_id:path}", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +async def get_file( + request: Request, + fastapi_response: Response, + file_id: str, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Returns information about a specific file. that can be used across - Assistants API, Batch API + This is the equivalent of GET https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/retrieve + + Example Curl + ``` + curl http://localhost:4000/v1/files/file-abc123 \ + -H "Authorization: Bearer sk-1234" + + ``` + """ + from litellm.proxy.proxy_server import ( + add_litellm_data_to_request, + general_settings, + get_custom_headers, + proxy_config, + proxy_logging_obj, + version, + ) + + data: Dict = {} + try: + + # Include original request and headers in the data + data = await add_litellm_data_to_request( + data=data, + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch + response = await litellm.afile_retrieve( + custom_llm_provider="openai", file_id=file_id, **data + ) + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" + ) + ) + + ### RESPONSE HEADERS ### + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + model_region=getattr(user_api_key_dict, "allowed_model_region", ""), + ) + ) + return response + + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.retrieve_file(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + if isinstance(e, HTTPException): + raise ProxyException( + message=getattr(e, "message", str(e.detail)), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + ) + else: + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 2a580f8e7be..2ba45d761de 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -106,7 +106,6 @@ import litellm from litellm import ( CancelBatchRequest, CreateBatchRequest, - CreateFileRequest, ListBatchRequest, RetrieveBatchRequest, ) @@ -174,6 +173,9 @@ from litellm.proxy.management_endpoints.key_management_endpoints import ( router as key_management_router, ) from litellm.proxy.management_endpoints.team_endpoints import router as team_router +from litellm.proxy.openai_files_endpoints.files_endpoints import ( + router as openai_files_router, +) from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( initialize_pass_through_endpoints, ) @@ -4870,117 +4872,6 @@ async def retrieve_batch( ###################################################################### -###################################################################### - -# /v1/files Endpoints - - -###################################################################### -@router.post( - "/v1/files", - dependencies=[Depends(user_api_key_auth)], - tags=["files"], -) -@router.post( - "/files", - dependencies=[Depends(user_api_key_auth)], - tags=["files"], -) -async def create_file( - request: Request, - fastapi_response: Response, - user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): - """ - Upload a file that can be used across - Assistants API, Batch API - This is the equivalent of POST https://api.openai.com/v1/files - - Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/create - - Example Curl - ``` - curl https://api.openai.com/v1/files \ - -H "Authorization: Bearer sk-1234" \ - -F purpose="batch" \ - -F file="@mydata.jsonl" - - ``` - """ - global proxy_logging_obj - data: Dict = {} - try: - # Use orjson to parse JSON data, orjson speeds up requests significantly - form_data = await request.form() - data = {key: value for key, value in form_data.items() if key != "file"} - - # Include original request and headers in the data - data = await add_litellm_data_to_request( - data=data, - request=request, - general_settings=general_settings, - user_api_key_dict=user_api_key_dict, - version=version, - proxy_config=proxy_config, - ) - - _create_file_request = CreateFileRequest() - - # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch - response = await litellm.acreate_file( - custom_llm_provider="openai", **_create_file_request - ) - - ### ALERTING ### - asyncio.create_task( - proxy_logging_obj.update_request_status( - litellm_call_id=data.get("litellm_call_id", ""), status="success" - ) - ) - - ### RESPONSE HEADERS ### - hidden_params = getattr(response, "_hidden_params", {}) or {} - model_id = hidden_params.get("model_id", None) or "" - cache_key = hidden_params.get("cache_key", None) or "" - api_base = hidden_params.get("api_base", None) or "" - - fastapi_response.headers.update( - get_custom_headers( - user_api_key_dict=user_api_key_dict, - model_id=model_id, - cache_key=cache_key, - api_base=api_base, - version=version, - model_region=getattr(user_api_key_dict, "allowed_model_region", ""), - ) - ) - - return response - except Exception as e: - await proxy_logging_obj.post_call_failure_hook( - user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data - ) - verbose_proxy_logger.error( - "litellm.proxy.proxy_server.create_file(): Exception occured - {}".format( - str(e) - ) - ) - verbose_proxy_logger.debug(traceback.format_exc()) - if isinstance(e, HTTPException): - raise ProxyException( - message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), - ) - else: - error_msg = f"{str(e)}" - raise ProxyException( - message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), - ) - @router.post( "/v1/moderations", @@ -9281,3 +9172,4 @@ app.include_router(caching_router) app.include_router(analytics_router) app.include_router(debugging_endpoints_router) app.include_router(ui_crud_endpoints_router) +app.include_router(openai_files_router) From efca9daf5d4d77756d16288de2dcf070d405c365 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 14:56:53 -0700 Subject: [PATCH 10/89] add "/v1/files/{file_id}" as openai route --- litellm/proxy/_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 39d48130bff..28e3914bfe1 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -204,6 +204,8 @@ class LiteLLMRoutes(enum.Enum): # files "/v1/files", "/files", + "/v1/files/{file_id}", + "/files/{file_id}", # assistants-related routes "/assistants", "/v1/assistants", From 5187569e11d2d4feb341ccccc6bbd64d6ca209dc Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:00:27 -0700 Subject: [PATCH 11/89] test retrieve file --- litellm/tests/test_openai_batches.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/litellm/tests/test_openai_batches.py b/litellm/tests/test_openai_batches.py index d7e3e18098d..91357f76318 100644 --- a/litellm/tests/test_openai_batches.py +++ b/litellm/tests/test_openai_batches.py @@ -1,22 +1,26 @@ # What is this? ## Unit Tests for OpenAI Batches API -import sys, os, json -import traceback import asyncio +import json +import os +import sys +import traceback + from dotenv import load_dotenv load_dotenv() sys.path.insert( 0, os.path.abspath("../..") ) # Adds the parent directory to the system path -import pytest, logging, asyncio -import litellm -from litellm import ( - create_batch, - create_file, -) +import asyncio +import logging import time +import pytest + +import litellm +from litellm import create_batch, create_file + def test_create_batch(): """ @@ -144,6 +148,13 @@ async def test_async_create_batch(): print("file content = ", file_content) + # file obj + file_obj = await litellm.afile_retrieve( + file_id=batch_input_file_id, custom_llm_provider="openai" + ) + print("file obj = ", file_obj) + assert file_obj.id == batch_input_file_id + # # write this file content to a file # with open("file_content.json", "w") as f: # json.dump(file_content, f) From 2db9c23bce450b1693141d7eb93d0d1d1af994d5 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 15:06:47 -0700 Subject: [PATCH 12/89] Remove unnecessary imports from `litellm/proxy/proxy_server.py` --- litellm/proxy/proxy_server.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index a600ac01897..4b756750ffb 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1,24 +1,18 @@ import ast import asyncio import copy -import hashlib -import importlib import inspect import os -import platform import random -import re import secrets -import shutil import subprocess import sys -import threading import time import traceback import uuid import warnings -from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Set, get_args +from datetime import datetime, timedelta +from typing import TYPE_CHECKING, Any, List, Optional import requests From a542e7be615be592f4f4bd49856d4684071365c3 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:35:21 -0700 Subject: [PATCH 13/89] add all openai file endpoints --- litellm/llms/openai.py | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/litellm/llms/openai.py b/litellm/llms/openai.py index 17f4f5159b4..0a2b6a3fa1e 100644 --- a/litellm/llms/openai.py +++ b/litellm/llms/openai.py @@ -18,6 +18,7 @@ import httpx import openai from openai import AsyncOpenAI, OpenAI from openai.types.beta.assistant_deleted import AssistantDeleted +from openai.types.file_deleted import FileDeleted from pydantic import BaseModel from typing_extensions import overload, override @@ -2110,6 +2111,105 @@ class OpenAIFilesAPI(BaseLLM): return response + async def adelete_file( + self, + file_id: str, + openai_client: AsyncOpenAI, + ) -> FileDeleted: + response = await openai_client.files.delete(file_id=file_id) + return response + + def delete_file( + self, + _is_async: bool, + file_id: str, + api_base: str, + api_key: Optional[str], + timeout: Union[float, httpx.Timeout], + max_retries: Optional[int], + organization: Optional[str], + client: Optional[Union[OpenAI, AsyncOpenAI]] = None, + ): + openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client( + api_key=api_key, + api_base=api_base, + timeout=timeout, + max_retries=max_retries, + organization=organization, + client=client, + _is_async=_is_async, + ) + if openai_client is None: + raise ValueError( + "OpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment." + ) + + if _is_async is True: + if not isinstance(openai_client, AsyncOpenAI): + raise ValueError( + "OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client." + ) + return self.adelete_file( # type: ignore + file_id=file_id, + openai_client=openai_client, + ) + response = openai_client.files.delete(file_id=file_id) + + return response + + async def alist_files( + self, + openai_client: AsyncOpenAI, + purpose: Optional[str] = None, + ): + if isinstance(purpose, str): + response = await openai_client.files.list(purpose=purpose) + else: + response = await openai_client.files.list() + return response + + def list_files( + self, + _is_async: bool, + api_base: str, + api_key: Optional[str], + timeout: Union[float, httpx.Timeout], + max_retries: Optional[int], + organization: Optional[str], + purpose: Optional[str] = None, + client: Optional[Union[OpenAI, AsyncOpenAI]] = None, + ): + openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client( + api_key=api_key, + api_base=api_base, + timeout=timeout, + max_retries=max_retries, + organization=organization, + client=client, + _is_async=_is_async, + ) + if openai_client is None: + raise ValueError( + "OpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment." + ) + + if _is_async is True: + if not isinstance(openai_client, AsyncOpenAI): + raise ValueError( + "OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client." + ) + return self.alist_files( # type: ignore + purpose=purpose, + openai_client=openai_client, + ) + + if isinstance(purpose, str): + response = openai_client.files.list(purpose=purpose) + else: + response = openai_client.files.list() + + return response + class OpenAIBatchesAPI(BaseLLM): """ From fc2b2fbe4992d61aa98d513ca3cd24e75bbed9ad Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:41:32 -0700 Subject: [PATCH 14/89] test - deleting a file --- litellm/batches/main.py | 283 ++--------------------------- litellm/files/main.py | 390 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 400 insertions(+), 273 deletions(-) diff --git a/litellm/batches/main.py b/litellm/batches/main.py index 4043606d531..af2dc5059d3 100644 --- a/litellm/batches/main.py +++ b/litellm/batches/main.py @@ -10,296 +10,37 @@ https://platform.openai.com/docs/api-reference/batch """ -import os import asyncio -from functools import partial import contextvars -from typing import Literal, Optional, Dict, Coroutine, Any, Union +import os +from functools import partial +from typing import Any, Coroutine, Dict, Literal, Optional, Union + import httpx import litellm from litellm import client from litellm.utils import supports_httpx_timeout -from ..types.router import * + from ..llms.openai import OpenAIBatchesAPI, OpenAIFilesAPI from ..types.llms.openai import ( - CreateBatchRequest, - RetrieveBatchRequest, - CancelBatchRequest, - CreateFileRequest, - FileTypes, - FileObject, Batch, + CancelBatchRequest, + CreateBatchRequest, + CreateFileRequest, FileContentRequest, + FileObject, + FileTypes, HttpxBinaryResponseContent, + RetrieveBatchRequest, ) +from ..types.router import * ####### ENVIRONMENT VARIABLES ################### openai_batches_instance = OpenAIBatchesAPI() -openai_files_instance = OpenAIFilesAPI() ################################################# -async def acreate_file( - file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune"], - custom_llm_provider: Literal["openai"] = "openai", - extra_headers: Optional[Dict[str, str]] = None, - extra_body: Optional[Dict[str, str]] = None, - **kwargs, -) -> Coroutine[Any, Any, FileObject]: - """ - Async: Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API. - - LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files - """ - try: - loop = asyncio.get_event_loop() - kwargs["acreate_file"] = True - - # Use a partial function to pass your keyword arguments - func = partial( - create_file, - file, - purpose, - custom_llm_provider, - extra_headers, - extra_body, - **kwargs, - ) - - # Add the context to the function - ctx = contextvars.copy_context() - func_with_context = partial(ctx.run, func) - init_response = await loop.run_in_executor(None, func_with_context) - if asyncio.iscoroutine(init_response): - response = await init_response - else: - response = init_response # type: ignore - - return response - except Exception as e: - raise e - - -def create_file( - file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune"], - custom_llm_provider: Literal["openai"] = "openai", - extra_headers: Optional[Dict[str, str]] = None, - extra_body: Optional[Dict[str, str]] = None, - **kwargs, -) -> Union[FileObject, Coroutine[Any, Any, FileObject]]: - """ - Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API. - - LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files - """ - try: - optional_params = GenericLiteLLMParams(**kwargs) - if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there - api_base = ( - optional_params.api_base - or litellm.api_base - or os.getenv("OPENAI_API_BASE") - or "https://api.openai.com/v1" - ) - organization = ( - optional_params.organization - or litellm.organization - or os.getenv("OPENAI_ORGANIZATION", None) - or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 - ) - # set API KEY - api_key = ( - optional_params.api_key - or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there - or litellm.openai_key - or os.getenv("OPENAI_API_KEY") - ) - ### TIMEOUT LOGIC ### - timeout = ( - optional_params.timeout or kwargs.get("request_timeout", 600) or 600 - ) - # set timeout for 10 minutes by default - - if ( - timeout is not None - and isinstance(timeout, httpx.Timeout) - and supports_httpx_timeout(custom_llm_provider) == False - ): - read_timeout = timeout.read or 600 - timeout = read_timeout # default 10 min timeout - elif timeout is not None and not isinstance(timeout, httpx.Timeout): - timeout = float(timeout) # type: ignore - elif timeout is None: - timeout = 600.0 - - _create_file_request = CreateFileRequest( - file=file, - purpose=purpose, - extra_headers=extra_headers, - extra_body=extra_body, - ) - - _is_async = kwargs.pop("acreate_file", False) is True - - response = openai_files_instance.create_file( - _is_async=_is_async, - api_base=api_base, - api_key=api_key, - timeout=timeout, - max_retries=optional_params.max_retries, - organization=organization, - create_file_data=_create_file_request, - ) - else: - raise litellm.exceptions.BadRequestError( - message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( - custom_llm_provider - ), - model="n/a", - llm_provider=custom_llm_provider, - response=httpx.Response( - status_code=400, - content="Unsupported provider", - request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore - ), - ) - return response - except Exception as e: - raise e - - -async def afile_content( - file_id: str, - custom_llm_provider: Literal["openai"] = "openai", - extra_headers: Optional[Dict[str, str]] = None, - extra_body: Optional[Dict[str, str]] = None, - **kwargs, -) -> Coroutine[Any, Any, HttpxBinaryResponseContent]: - """ - Async: Get file contents - - LiteLLM Equivalent of GET https://api.openai.com/v1/files - """ - try: - loop = asyncio.get_event_loop() - kwargs["afile_content"] = True - - # Use a partial function to pass your keyword arguments - func = partial( - file_content, - file_id, - custom_llm_provider, - extra_headers, - extra_body, - **kwargs, - ) - - # Add the context to the function - ctx = contextvars.copy_context() - func_with_context = partial(ctx.run, func) - init_response = await loop.run_in_executor(None, func_with_context) - if asyncio.iscoroutine(init_response): - response = await init_response - else: - response = init_response # type: ignore - - return response - except Exception as e: - raise e - - -def file_content( - file_id: str, - custom_llm_provider: Literal["openai"] = "openai", - extra_headers: Optional[Dict[str, str]] = None, - extra_body: Optional[Dict[str, str]] = None, - **kwargs, -) -> Union[HttpxBinaryResponseContent, Coroutine[Any, Any, HttpxBinaryResponseContent]]: - """ - Returns the contents of the specified file. - - LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files - """ - try: - optional_params = GenericLiteLLMParams(**kwargs) - if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there - api_base = ( - optional_params.api_base - or litellm.api_base - or os.getenv("OPENAI_API_BASE") - or "https://api.openai.com/v1" - ) - organization = ( - optional_params.organization - or litellm.organization - or os.getenv("OPENAI_ORGANIZATION", None) - or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 - ) - # set API KEY - api_key = ( - optional_params.api_key - or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there - or litellm.openai_key - or os.getenv("OPENAI_API_KEY") - ) - ### TIMEOUT LOGIC ### - timeout = ( - optional_params.timeout or kwargs.get("request_timeout", 600) or 600 - ) - # set timeout for 10 minutes by default - - if ( - timeout is not None - and isinstance(timeout, httpx.Timeout) - and supports_httpx_timeout(custom_llm_provider) == False - ): - read_timeout = timeout.read or 600 - timeout = read_timeout # default 10 min timeout - elif timeout is not None and not isinstance(timeout, httpx.Timeout): - timeout = float(timeout) # type: ignore - elif timeout is None: - timeout = 600.0 - - _file_content_request = FileContentRequest( - file_id=file_id, - extra_headers=extra_headers, - extra_body=extra_body, - ) - - _is_async = kwargs.pop("afile_content", False) is True - - response = openai_files_instance.file_content( - _is_async=_is_async, - file_content_request=_file_content_request, - api_base=api_base, - api_key=api_key, - timeout=timeout, - max_retries=optional_params.max_retries, - organization=organization, - ) - else: - raise litellm.exceptions.BadRequestError( - message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( - custom_llm_provider - ), - model="n/a", - llm_provider=custom_llm_provider, - response=httpx.Response( - status_code=400, - content="Unsupported provider", - request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore - ), - ) - return response - except Exception as e: - raise e - - async def acreate_batch( completion_window: Literal["24h"], endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], diff --git a/litellm/files/main.py b/litellm/files/main.py index e11833eb446..c4c4c32edcf 100644 --- a/litellm/files/main.py +++ b/litellm/files/main.py @@ -12,11 +12,10 @@ from functools import partial from typing import Any, Coroutine, Dict, Literal, Optional, Union import httpx -from openai.types.file_object import FileObject import litellm from litellm import client -from litellm.batches.main import openai_files_instance +from litellm.llms.openai import FileDeleted, FileObject, OpenAIFilesAPI from litellm.types.llms.openai import ( Batch, CreateFileRequest, @@ -27,6 +26,10 @@ from litellm.types.llms.openai import ( from litellm.types.router import * from litellm.utils import supports_httpx_timeout +####### ENVIRONMENT VARIABLES ################### +openai_files_instance = OpenAIFilesAPI() +################################################# + async def afile_retrieve( file_id: str, @@ -148,3 +151,386 @@ def file_retrieve( return response except Exception as e: raise e + + +# Delete file +async def afile_delete( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Coroutine[Any, Any, FileObject]: + """ + Async: Delete file + + LiteLLM Equivalent of DELETE https://api.openai.com/v1/files + """ + try: + loop = asyncio.get_event_loop() + kwargs["is_async"] = True + + # Use a partial function to pass your keyword arguments + func = partial( + file_delete, + file_id, + custom_llm_provider, + extra_headers, + extra_body, + **kwargs, + ) + + # Add the context to the function + ctx = contextvars.copy_context() + func_with_context = partial(ctx.run, func) + init_response = await loop.run_in_executor(None, func_with_context) + if asyncio.iscoroutine(init_response): + response = await init_response + else: + response = init_response # type: ignore + + return response + except Exception as e: + raise e + + +def file_delete( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> FileDeleted: + """ + Delete file + + LiteLLM Equivalent of DELETE https://api.openai.com/v1/files + """ + try: + optional_params = GenericLiteLLMParams(**kwargs) + if custom_llm_provider == "openai": + # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there + api_base = ( + optional_params.api_base + or litellm.api_base + or os.getenv("OPENAI_API_BASE") + or "https://api.openai.com/v1" + ) + organization = ( + optional_params.organization + or litellm.organization + or os.getenv("OPENAI_ORGANIZATION", None) + or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 + ) + # set API KEY + api_key = ( + optional_params.api_key + or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there + or litellm.openai_key + or os.getenv("OPENAI_API_KEY") + ) + ### TIMEOUT LOGIC ### + timeout = ( + optional_params.timeout or kwargs.get("request_timeout", 600) or 600 + ) + # set timeout for 10 minutes by default + + if ( + timeout is not None + and isinstance(timeout, httpx.Timeout) + and supports_httpx_timeout(custom_llm_provider) == False + ): + read_timeout = timeout.read or 600 + timeout = read_timeout # default 10 min timeout + elif timeout is not None and not isinstance(timeout, httpx.Timeout): + timeout = float(timeout) # type: ignore + elif timeout is None: + timeout = 600.0 + + _is_async = kwargs.pop("is_async", False) is True + + response = openai_files_instance.delete_file( + file_id=file_id, + _is_async=_is_async, + api_base=api_base, + api_key=api_key, + timeout=timeout, + max_retries=optional_params.max_retries, + organization=organization, + ) + else: + raise litellm.exceptions.BadRequestError( + message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( + custom_llm_provider + ), + model="n/a", + llm_provider=custom_llm_provider, + response=httpx.Response( + status_code=400, + content="Unsupported provider", + request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore + ), + ) + return response + except Exception as e: + raise e + + +async def acreate_file( + file: FileTypes, + purpose: Literal["assistants", "batch", "fine-tune"], + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Coroutine[Any, Any, FileObject]: + """ + Async: Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API. + + LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files + """ + try: + loop = asyncio.get_event_loop() + kwargs["acreate_file"] = True + + # Use a partial function to pass your keyword arguments + func = partial( + create_file, + file, + purpose, + custom_llm_provider, + extra_headers, + extra_body, + **kwargs, + ) + + # Add the context to the function + ctx = contextvars.copy_context() + func_with_context = partial(ctx.run, func) + init_response = await loop.run_in_executor(None, func_with_context) + if asyncio.iscoroutine(init_response): + response = await init_response + else: + response = init_response # type: ignore + + return response + except Exception as e: + raise e + + +def create_file( + file: FileTypes, + purpose: Literal["assistants", "batch", "fine-tune"], + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Union[FileObject, Coroutine[Any, Any, FileObject]]: + """ + Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API. + + LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files + """ + try: + optional_params = GenericLiteLLMParams(**kwargs) + if custom_llm_provider == "openai": + # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there + api_base = ( + optional_params.api_base + or litellm.api_base + or os.getenv("OPENAI_API_BASE") + or "https://api.openai.com/v1" + ) + organization = ( + optional_params.organization + or litellm.organization + or os.getenv("OPENAI_ORGANIZATION", None) + or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 + ) + # set API KEY + api_key = ( + optional_params.api_key + or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there + or litellm.openai_key + or os.getenv("OPENAI_API_KEY") + ) + ### TIMEOUT LOGIC ### + timeout = ( + optional_params.timeout or kwargs.get("request_timeout", 600) or 600 + ) + # set timeout for 10 minutes by default + + if ( + timeout is not None + and isinstance(timeout, httpx.Timeout) + and supports_httpx_timeout(custom_llm_provider) == False + ): + read_timeout = timeout.read or 600 + timeout = read_timeout # default 10 min timeout + elif timeout is not None and not isinstance(timeout, httpx.Timeout): + timeout = float(timeout) # type: ignore + elif timeout is None: + timeout = 600.0 + + _create_file_request = CreateFileRequest( + file=file, + purpose=purpose, + extra_headers=extra_headers, + extra_body=extra_body, + ) + + _is_async = kwargs.pop("acreate_file", False) is True + + response = openai_files_instance.create_file( + _is_async=_is_async, + api_base=api_base, + api_key=api_key, + timeout=timeout, + max_retries=optional_params.max_retries, + organization=organization, + create_file_data=_create_file_request, + ) + else: + raise litellm.exceptions.BadRequestError( + message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( + custom_llm_provider + ), + model="n/a", + llm_provider=custom_llm_provider, + response=httpx.Response( + status_code=400, + content="Unsupported provider", + request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore + ), + ) + return response + except Exception as e: + raise e + + +async def afile_content( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Coroutine[Any, Any, HttpxBinaryResponseContent]: + """ + Async: Get file contents + + LiteLLM Equivalent of GET https://api.openai.com/v1/files + """ + try: + loop = asyncio.get_event_loop() + kwargs["afile_content"] = True + + # Use a partial function to pass your keyword arguments + func = partial( + file_content, + file_id, + custom_llm_provider, + extra_headers, + extra_body, + **kwargs, + ) + + # Add the context to the function + ctx = contextvars.copy_context() + func_with_context = partial(ctx.run, func) + init_response = await loop.run_in_executor(None, func_with_context) + if asyncio.iscoroutine(init_response): + response = await init_response + else: + response = init_response # type: ignore + + return response + except Exception as e: + raise e + + +def file_content( + file_id: str, + custom_llm_provider: Literal["openai"] = "openai", + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +) -> Union[HttpxBinaryResponseContent, Coroutine[Any, Any, HttpxBinaryResponseContent]]: + """ + Returns the contents of the specified file. + + LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files + """ + try: + optional_params = GenericLiteLLMParams(**kwargs) + if custom_llm_provider == "openai": + # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there + api_base = ( + optional_params.api_base + or litellm.api_base + or os.getenv("OPENAI_API_BASE") + or "https://api.openai.com/v1" + ) + organization = ( + optional_params.organization + or litellm.organization + or os.getenv("OPENAI_ORGANIZATION", None) + or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 + ) + # set API KEY + api_key = ( + optional_params.api_key + or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there + or litellm.openai_key + or os.getenv("OPENAI_API_KEY") + ) + ### TIMEOUT LOGIC ### + timeout = ( + optional_params.timeout or kwargs.get("request_timeout", 600) or 600 + ) + # set timeout for 10 minutes by default + + if ( + timeout is not None + and isinstance(timeout, httpx.Timeout) + and supports_httpx_timeout(custom_llm_provider) == False + ): + read_timeout = timeout.read or 600 + timeout = read_timeout # default 10 min timeout + elif timeout is not None and not isinstance(timeout, httpx.Timeout): + timeout = float(timeout) # type: ignore + elif timeout is None: + timeout = 600.0 + + _file_content_request = FileContentRequest( + file_id=file_id, + extra_headers=extra_headers, + extra_body=extra_body, + ) + + _is_async = kwargs.pop("afile_content", False) is True + + response = openai_files_instance.file_content( + _is_async=_is_async, + file_content_request=_file_content_request, + api_base=api_base, + api_key=api_key, + timeout=timeout, + max_retries=optional_params.max_retries, + organization=organization, + ) + else: + raise litellm.exceptions.BadRequestError( + message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format( + custom_llm_provider + ), + model="n/a", + llm_provider=custom_llm_provider, + response=httpx.Response( + status_code=400, + content="Unsupported provider", + request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore + ), + ) + return response + except Exception as e: + raise e From f18754b6ed4e1c238fb1038029f717f3f2d4a8b5 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:42:15 -0700 Subject: [PATCH 15/89] test - delete file --- litellm/tests/test_openai_batches.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/litellm/tests/test_openai_batches.py b/litellm/tests/test_openai_batches.py index 91357f76318..05a81a50e68 100644 --- a/litellm/tests/test_openai_batches.py +++ b/litellm/tests/test_openai_batches.py @@ -155,6 +155,15 @@ async def test_async_create_batch(): print("file obj = ", file_obj) assert file_obj.id == batch_input_file_id + # delete file + delete_file_response = await litellm.afile_delete( + file_id=batch_input_file_id, custom_llm_provider="openai" + ) + + print("delete file response = ", delete_file_response) + + assert delete_file_response.id == batch_input_file_id + # # write this file content to a file # with open("file_content.json", "w") as f: # json.dump(file_content, f) From ef3bd2df229ed7b93b02d1eeabc565f79ce25681 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:51:28 -0700 Subject: [PATCH 16/89] support list files on litellm SDK --- litellm/files/main.py | 123 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/litellm/files/main.py b/litellm/files/main.py index c4c4c32edcf..598bc48787a 100644 --- a/litellm/files/main.py +++ b/litellm/files/main.py @@ -276,6 +276,129 @@ def file_delete( raise e +# List files +async def afile_list( + custom_llm_provider: Literal["openai"] = "openai", + purpose: Optional[str] = None, + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +): + """ + Async: List files + + LiteLLM Equivalent of GET https://api.openai.com/v1/files + """ + try: + loop = asyncio.get_event_loop() + kwargs["is_async"] = True + + # Use a partial function to pass your keyword arguments + func = partial( + file_list, + custom_llm_provider, + purpose, + extra_headers, + extra_body, + **kwargs, + ) + + # Add the context to the function + ctx = contextvars.copy_context() + func_with_context = partial(ctx.run, func) + init_response = await loop.run_in_executor(None, func_with_context) + if asyncio.iscoroutine(init_response): + response = await init_response + else: + response = init_response # type: ignore + + return response + except Exception as e: + raise e + + +def file_list( + custom_llm_provider: Literal["openai"] = "openai", + purpose: Optional[str] = None, + extra_headers: Optional[Dict[str, str]] = None, + extra_body: Optional[Dict[str, str]] = None, + **kwargs, +): + """ + List files + + LiteLLM Equivalent of GET https://api.openai.com/v1/files + """ + try: + optional_params = GenericLiteLLMParams(**kwargs) + if custom_llm_provider == "openai": + # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there + api_base = ( + optional_params.api_base + or litellm.api_base + or os.getenv("OPENAI_API_BASE") + or "https://api.openai.com/v1" + ) + organization = ( + optional_params.organization + or litellm.organization + or os.getenv("OPENAI_ORGANIZATION", None) + or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105 + ) + # set API KEY + api_key = ( + optional_params.api_key + or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there + or litellm.openai_key + or os.getenv("OPENAI_API_KEY") + ) + ### TIMEOUT LOGIC ### + timeout = ( + optional_params.timeout or kwargs.get("request_timeout", 600) or 600 + ) + # set timeout for 10 minutes by default + + if ( + timeout is not None + and isinstance(timeout, httpx.Timeout) + and supports_httpx_timeout(custom_llm_provider) == False + ): + read_timeout = timeout.read or 600 + timeout = read_timeout # default 10 min timeout + elif timeout is not None and not isinstance(timeout, httpx.Timeout): + timeout = float(timeout) # type: ignore + elif timeout is None: + timeout = 600.0 + + _is_async = kwargs.pop("is_async", False) is True + + response = openai_files_instance.list_files( + purpose=purpose, + _is_async=_is_async, + api_base=api_base, + api_key=api_key, + timeout=timeout, + max_retries=optional_params.max_retries, + organization=organization, + ) + else: + raise litellm.exceptions.BadRequestError( + message="LiteLLM doesn't support {} for 'file_list'. Only 'openai' is supported.".format( + custom_llm_provider + ), + model="n/a", + llm_provider=custom_llm_provider, + response=httpx.Response( + status_code=400, + content="Unsupported provider", + request=httpx.Request(method="file_list", url="https://github.com/BerriAI/litellm"), # type: ignore + ), + ) + return response + except Exception as e: + raise e + + async def acreate_file( file: FileTypes, purpose: Literal["assistants", "batch", "fine-tune"], From a7415865191cc6549824c17f9e8b06c7f56c9798 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 15:54:55 -0700 Subject: [PATCH 17/89] test openai files endpoints --- ...t_openai_batches.py => test_openai_batches_and_files.py} | 6 ++++++ 1 file changed, 6 insertions(+) rename litellm/tests/{test_openai_batches.py => test_openai_batches_and_files.py} (97%) diff --git a/litellm/tests/test_openai_batches.py b/litellm/tests/test_openai_batches_and_files.py similarity index 97% rename from litellm/tests/test_openai_batches.py rename to litellm/tests/test_openai_batches_and_files.py index 05a81a50e68..14aee6e19f1 100644 --- a/litellm/tests/test_openai_batches.py +++ b/litellm/tests/test_openai_batches_and_files.py @@ -164,6 +164,12 @@ async def test_async_create_batch(): assert delete_file_response.id == batch_input_file_id + all_files_list = await litellm.afile_list( + custom_llm_provider="openai", + ) + + print("all_files_list = ", all_files_list) + # # write this file content to a file # with open("file_content.json", "w") as f: # json.dump(file_content, f) From 0b39ec6a8e3d71801ae7ff53709c1a8b646c43bf Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:04:07 -0700 Subject: [PATCH 18/89] add /v1/files LIST --- .../openai_files_endpoints/files_endpoints.py | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py index d8f0a7e287e..073bf400302 100644 --- a/litellm/proxy/openai_files_endpoints/files_endpoints.py +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -263,3 +263,111 @@ async def get_file( param=getattr(e, "param", "None"), code=getattr(e, "status_code", 500), ) + + +@router.get( + "/v1/files", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +@router.get( + "/files", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +async def list_files( + request: Request, + fastapi_response: Response, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), + purpose: Optional[str] = None, +): + """ + Returns information about a specific file. that can be used across - Assistants API, Batch API + This is the equivalent of GET https://api.openai.com/v1/files/ + + Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/list + + Example Curl + ``` + curl http://localhost:4000/v1/files\ + -H "Authorization: Bearer sk-1234" + + ``` + """ + from litellm.proxy.proxy_server import ( + add_litellm_data_to_request, + general_settings, + get_custom_headers, + proxy_config, + proxy_logging_obj, + version, + ) + + data: Dict = {} + try: + + # Include original request and headers in the data + data = await add_litellm_data_to_request( + data=data, + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch + response = await litellm.afile_list( + custom_llm_provider="openai", purpose=purpose, **data + ) + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" + ) + ) + + ### RESPONSE HEADERS ### + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + model_region=getattr(user_api_key_dict, "allowed_model_region", ""), + ) + ) + return response + + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.list_files(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + if isinstance(e, HTTPException): + raise ProxyException( + message=getattr(e, "message", str(e.detail)), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + ) + else: + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) From cb300d30a95c7af0c6e5b1af80032a219628a820 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:08:58 -0700 Subject: [PATCH 19/89] add file delete path --- .../openai_files_endpoints/files_endpoints.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py index 073bf400302..36972972bd7 100644 --- a/litellm/proxy/openai_files_endpoints/files_endpoints.py +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -265,6 +265,115 @@ async def get_file( ) +@router.delete( + "/v1/files/{file_id:path}", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +@router.delete( + "/files/{file_id:path}", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +async def delete_file( + request: Request, + fastapi_response: Response, + file_id: str, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Deletes a specified file. that can be used across - Assistants API, Batch API + This is the equivalent of DELETE https://api.openai.com/v1/files/{file_id} + + Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/delete + + Example Curl + ``` + curl http://localhost:4000/v1/files/file-abc123 \ + -X DELETE \ + -H "Authorization: Bearer $OPENAI_API_KEY" + + ``` + """ + from litellm.proxy.proxy_server import ( + add_litellm_data_to_request, + general_settings, + get_custom_headers, + proxy_config, + proxy_logging_obj, + version, + ) + + data: Dict = {} + try: + + # Include original request and headers in the data + data = await add_litellm_data_to_request( + data=data, + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch + response = await litellm.afile_delete( + custom_llm_provider="openai", file_id=file_id, **data + ) + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" + ) + ) + + ### RESPONSE HEADERS ### + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + model_region=getattr(user_api_key_dict, "allowed_model_region", ""), + ) + ) + return response + + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.retrieve_file(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + if isinstance(e, HTTPException): + raise ProxyException( + message=getattr(e, "message", str(e.detail)), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + ) + else: + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) + + @router.get( "/v1/files", dependencies=[Depends(user_api_key_auth)], From a5f12aba81ff2290068fd6523cbbb27d31c99736 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:15:43 -0700 Subject: [PATCH 20/89] add file content --- .../openai_files_endpoints/files_endpoints.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py index 36972972bd7..e4ac8fc8042 100644 --- a/litellm/proxy/openai_files_endpoints/files_endpoints.py +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -480,3 +480,120 @@ async def list_files( param=getattr(e, "param", "None"), code=getattr(e, "status_code", 500), ) + + +@router.get( + "/v1/files/{file_id:path}/content", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +@router.get( + "/files/{file_id:path}/content", + dependencies=[Depends(user_api_key_auth)], + tags=["files"], +) +async def get_file_content( + request: Request, + fastapi_response: Response, + file_id: str, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """ + Returns information about a specific file. that can be used across - Assistants API, Batch API + This is the equivalent of GET https://api.openai.com/v1/files/{file_id}/content + + Supports Identical Params as: https://platform.openai.com/docs/api-reference/files/retrieve-contents + + Example Curl + ``` + curl http://localhost:4000/v1/files/file-abc123/content \ + -H "Authorization: Bearer sk-1234" + + ``` + """ + from litellm.proxy.proxy_server import ( + add_litellm_data_to_request, + general_settings, + get_custom_headers, + proxy_config, + proxy_logging_obj, + version, + ) + + data: Dict = {} + try: + + # Include original request and headers in the data + data = await add_litellm_data_to_request( + data=data, + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # for now use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for acreate_batch + response = await litellm.afile_content( + custom_llm_provider="openai", file_id=file_id, **data + ) + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" + ) + ) + + ### RESPONSE HEADERS ### + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + model_region=getattr(user_api_key_dict, "allowed_model_region", ""), + ) + ) + httpx_response: Optional[httpx.Response] = getattr(response, "response", None) + if httpx_response is None: + raise ValueError( + f"Invalid response - response.response is None - got {response}" + ) + return Response( + content=httpx_response.content, + status_code=httpx_response.status_code, + headers=httpx_response.headers, + ) + + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.retrieve_file_content(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + if isinstance(e, HTTPException): + raise ProxyException( + message=getattr(e, "message", str(e.detail)), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + ) + else: + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) From f34e45db938598403734c43fd88b587a38404c42 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:36:27 -0700 Subject: [PATCH 21/89] test - /file endpoints --- tests/test_openai_files_endpoints.py | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/test_openai_files_endpoints.py diff --git a/tests/test_openai_files_endpoints.py b/tests/test_openai_files_endpoints.py new file mode 100644 index 00000000000..d3922ab6994 --- /dev/null +++ b/tests/test_openai_files_endpoints.py @@ -0,0 +1,97 @@ +# What this tests ? +## Tests /chat/completions by generating a key and then making a chat completions request +import pytest +import asyncio +import aiohttp, openai +from openai import OpenAI, AsyncOpenAI +from typing import Optional, List, Union + + +BASE_URL = "http://localhost:4000" # Replace with your actual base URL +API_KEY = "sk-1234" # Replace with your actual API key + + +@pytest.mark.asyncio +async def test_file_operations(): + async with aiohttp.ClientSession() as session: + # Test file upload and get file_id + file_id = await upload_file(session) + + # Test list files + await list_files(session) + + # Test get file + await get_file(session, file_id) + + # Test get file content + await get_file_content(session, file_id) + + # Test delete file + await delete_file(session, file_id) + + +async def upload_file(session): + url = f"{BASE_URL}/v1/files" + headers = {"Authorization": f"Bearer {API_KEY}"} + data = aiohttp.FormData() + data.add_field("purpose", "fine-tune") + data.add_field( + "file", b'{"prompt": "Hello", "completion": "Hi"}', filename="mydata.jsonl" + ) + + async with session.post(url, headers=headers, data=data) as response: + assert response.status == 200 + result = await response.json() + assert "id" in result + print(f"File upload successful. File ID: {result['id']}") + return result["id"] + + +async def list_files(session): + url = f"{BASE_URL}/v1/files" + headers = {"Authorization": f"Bearer {API_KEY}"} + + async with session.get(url, headers=headers) as response: + assert response.status == 200 + result = await response.json() + assert "data" in result + print("List files successful") + + +async def get_file(session, file_id): + url = f"{BASE_URL}/v1/files/{file_id}" + headers = {"Authorization": f"Bearer {API_KEY}"} + + async with session.get(url, headers=headers) as response: + assert response.status == 200 + result = await response.json() + assert result["id"] == file_id + assert result["object"] == "file" + assert "bytes" in result + assert "created_at" in result + assert "filename" in result + assert result["purpose"] == "fine-tune" + print(f"Get file successful for file ID: {file_id}") + + +async def get_file_content(session, file_id): + url = f"{BASE_URL}/v1/files/{file_id}/content" + headers = {"Authorization": f"Bearer {API_KEY}"} + + async with session.get(url, headers=headers) as response: + assert response.status == 200 + content = await response.text() + assert content # Check if content is not empty + print(f"Get file content successful for file ID: {file_id}") + + +async def delete_file(session, file_id): + url = f"{BASE_URL}/v1/files/{file_id}" + headers = {"Authorization": f"Bearer {API_KEY}"} + + async with session.delete(url, headers=headers) as response: + assert response.status == 200 + result = await response.json() + assert "deleted" in result + assert result["id"] == file_id + print(f"Delete file successful for file ID: {file_id}") From 265ec00d0f49b0200d771942e5cac60139f51854 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:50:46 -0700 Subject: [PATCH 22/89] fix test routes on litellm proxy --- litellm/proxy/proxy_server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c9960f595f8..69f63d98531 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3239,6 +3239,12 @@ async def completion( response_class=ORJSONResponse, tags=["embeddings"], ) +@router.post( + "/engines/{model:path}/embeddings", + dependencies=[Depends(user_api_key_auth)], + response_class=ORJSONResponse, + tags=["embeddings"], +) # azure compatible endpoint @router.post( "/openai/deployments/{model:path}/embeddings", dependencies=[Depends(user_api_key_auth)], From 7b8670f8831399c4d4395694d3614977a2a40120 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:55:09 -0700 Subject: [PATCH 23/89] add /files/{file_id}/content as openai route --- litellm/proxy/_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 28e3914bfe1..c3bffc516ca 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -206,6 +206,8 @@ class LiteLLMRoutes(enum.Enum): "/files", "/v1/files/{file_id}", "/files/{file_id}", + "/v1/files/{file_id}/content", + "/files/{file_id}/content", # assistants-related routes "/assistants", "/v1/assistants", From de0dacc42d1f743c7479cb60e327ea96f9875051 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 16:58:53 -0700 Subject: [PATCH 24/89] fix test proxy routes --- litellm/tests/test_proxy_routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/tests/test_proxy_routes.py b/litellm/tests/test_proxy_routes.py index fb10610f379..776ad1e788f 100644 --- a/litellm/tests/test_proxy_routes.py +++ b/litellm/tests/test_proxy_routes.py @@ -20,7 +20,7 @@ import pytest import litellm from litellm.proxy._types import LiteLLMRoutes from litellm.proxy.auth.auth_utils import is_openai_route -from litellm.proxy.proxy_server import router +from litellm.proxy.proxy_server import app # Configure logging logging.basicConfig( @@ -37,7 +37,7 @@ def test_routes_on_litellm_proxy(): this prevents accidentelly deleting /threads, or /batches etc """ _all_routes = [] - for route in router.routes: + for route in app.routes: _path_as_str = str(route.path) if ":path" in _path_as_str: From dd0c07d2a1664bdfa24830e1d2ba7de14ddbfd65 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 17:02:56 -0700 Subject: [PATCH 25/89] Move JSX stuff so first line of file is heading This prevents VS Code from displaying a warning about the file not starting with a heading. --- docs/my-website/docs/proxy/logging.md | 9 ++++----- litellm/proxy/proxy_server.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index c2f583366cf..7a9c7cf7a88 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -1,12 +1,11 @@ -import Image from '@theme/IdealImage'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - # 🪢 Logging - Langfuse, OpenTelemetry, Custom Callbacks, DataDog, s3 Bucket, Sentry, Athina, Azure Content-Safety Log Proxy Input, Output, Exceptions using Langfuse, OpenTelemetry, Custom Callbacks, DataDog, DynamoDB, s3 Bucket +import Image from '@theme/IdealImage'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + ## Table of Contents - [Logging to Langfuse](#logging-proxy-inputoutput---langfuse) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 69f63d98531..fd445b51b15 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2791,7 +2791,7 @@ async def chat_completion( ## LOGGING OBJECT ## - initialize logging object for logging success/failure events for call ## IMPORTANT Note: - initialize this before running pre-call checks. Ensures we log rejected requests to langfuse. - data["litellm_call_id"] = str(uuid.uuid4()) + data["litellm_call_id"] = request.headers.get('x-litellm-call-id', str(uuid.uuid4())) logging_obj, data = litellm.utils.function_setup( original_function="acompletion", rules_obj=litellm.utils.Rules(), From 3e49cfa6ff2714286f65faa8187a4b5d44b168f2 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 17:03:47 -0700 Subject: [PATCH 26/89] Delete manual table of contents It seems redudant since Docusaurus already generates a table of contents in the right sidebar. In fact, the `(BETA) Moderation with Azure Content-Safety` link in the manual TOC was broken, which shows how easy it is to forget to update the manual TOC when adding new content or to make a mistake while doing it. --- docs/my-website/docs/proxy/logging.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 7a9c7cf7a88..d162d14efe2 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -6,21 +6,6 @@ import Image from '@theme/IdealImage'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -## Table of Contents - -- [Logging to Langfuse](#logging-proxy-inputoutput---langfuse) -- [Logging with OpenTelemetry (OpenTelemetry)](#logging-proxy-inputoutput-in-opentelemetry-format) -- [Async Custom Callbacks](#custom-callback-class-async) -- [Async Custom Callback APIs](#custom-callback-apis-async) -- [Logging to Galileo](#logging-llm-io-to-galileo) -- [Logging to OpenMeter](#logging-proxy-inputoutput---langfuse) -- [Logging to s3 Buckets](#logging-proxy-inputoutput---s3-buckets) -- [Logging to DataDog](#logging-proxy-inputoutput---datadog) -- [Logging to DynamoDB](#logging-proxy-inputoutput---dynamodb) -- [Logging to Sentry](#logging-proxy-inputoutput---sentry) -- [Logging to Athina](#logging-proxy-inputoutput-athina) -- [(BETA) Moderation with Azure Content-Safety](#moderation-with-azure-content-safety) - ## Logging Proxy Input/Output - Langfuse We will use the `--config` to set `litellm.success_callback = ["langfuse"]` this will log all successfull LLM calls to langfuse. Make sure to set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in your environment From 9dadbe52f7797abf8da6f85035467dca16016f1c Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 17:07:51 -0700 Subject: [PATCH 27/89] Shorten title I think it looks better to have a shorter title in the left sidebar and then describe the longer details in the body of the document. --- docs/my-website/docs/proxy/logging.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index d162d14efe2..41d1c7db565 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -1,6 +1,14 @@ -# 🪢 Logging - Langfuse, OpenTelemetry, Custom Callbacks, DataDog, s3 Bucket, Sentry, Athina, Azure Content-Safety +# 🪢 Logging -Log Proxy Input, Output, Exceptions using Langfuse, OpenTelemetry, Custom Callbacks, DataDog, DynamoDB, s3 Bucket +Log Proxy input, output, and exceptions using: + +- Langfuse +- OpenTelemetry +- Custom Callbacks +- DataDog +- DynamoDB +- s3 Bucket +- etc. import Image from '@theme/IdealImage'; import Tabs from '@theme/Tabs'; From 7efe9beac57e5ffe86eccb41019b5157e157df46 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 17:14:53 -0700 Subject: [PATCH 28/89] fix test_bedrock_httpx_streaming --- litellm/llms/bedrock_httpx.py | 2 +- litellm/tests/test_streaming.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/litellm/llms/bedrock_httpx.py b/litellm/llms/bedrock_httpx.py index 4e737d1d247..33d9203381b 100644 --- a/litellm/llms/bedrock_httpx.py +++ b/litellm/llms/bedrock_httpx.py @@ -1066,7 +1066,7 @@ class BedrockLLM(BaseLLM): if response.status_code != 200: raise BedrockError( - status_code=response.status_code, message=response.text + status_code=response.status_code, message=response.read() ) decoder = AWSEventStreamDecoder(model=model) diff --git a/litellm/tests/test_streaming.py b/litellm/tests/test_streaming.py index a9540b20a1e..1b0f91fe140 100644 --- a/litellm/tests/test_streaming.py +++ b/litellm/tests/test_streaming.py @@ -21,6 +21,8 @@ sys.path.insert( from dotenv import load_dotenv load_dotenv() +import random + import litellm from litellm import ( AuthenticationError, @@ -1373,7 +1375,8 @@ async def test_bedrock_httpx_streaming(sync_mode, model): if complete_response.strip() == "": raise Exception("Empty response received") print(f"completion_response: {complete_response}\n\nFinalChunk: {final_chunk}") - except RateLimitError: + except RateLimitError as e: + print("got rate limit error=", e) pass except Exception as e: pytest.fail(f"Error occurred: {e}") From 45a992008001cd68e5c7d183f01fc3ce8e1f77b1 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 17:20:23 -0700 Subject: [PATCH 29/89] Add/remove blank lines to make MD linter happy This removed a lot of yellow squigglies in my VS Code. --- docs/my-website/docs/proxy/logging.md | 84 +++++++++++++++++---------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 41d1c7db565..8a5a06a8e61 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -15,6 +15,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ## Logging Proxy Input/Output - Langfuse + We will use the `--config` to set `litellm.success_callback = ["langfuse"]` this will log all successfull LLM calls to langfuse. Make sure to set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in your environment **Step 1** Install langfuse @@ -24,6 +25,7 @@ pip install langfuse>=2.0.0 ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -34,6 +36,7 @@ litellm_settings: ``` **Step 3**: Set required env variables for logging to langfuse + ```shell export LANGFUSE_PUBLIC_KEY="pk_kk" export LANGFUSE_SECRET_KEY="sk_ss" @@ -44,11 +47,13 @@ export LANGFUSE_HOST="https://xxx.langfuse.com" **Step 4**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ``` litellm --test ``` @@ -59,7 +64,6 @@ Expected output on Langfuse ### Logging Metadata to Langfuse - @@ -85,6 +89,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ } }' ``` + @@ -118,6 +123,7 @@ response = client.chat.completions.create( print(response) ``` + @@ -160,7 +166,6 @@ print(response) - ### Team based Logging to Langfuse **Example:** @@ -249,6 +254,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ } }' ``` + @@ -279,6 +285,7 @@ response = client.chat.completions.create( print(response) ``` + @@ -324,7 +331,6 @@ You will see `raw_request` in your Langfuse Metadata. This is the RAW CURL comma - ## Logging Proxy Input/Output in OpenTelemetry format :::info @@ -340,10 +346,8 @@ OTEL_SERVICE_NAME=` # default="litellm" - - **Step 1:** Set callbacks and env vars Add the following to your env @@ -359,7 +363,6 @@ litellm_settings: callbacks: ["otel"] ``` - **Step 2**: Start the proxy, make a test request Start proxy @@ -419,7 +422,6 @@ This is the Span from OTEL Logging - #### Quick Start - Log to Honeycomb @@ -441,7 +443,6 @@ litellm_settings: callbacks: ["otel"] ``` - **Step 2**: Start the proxy, make a test request Start proxy @@ -466,10 +467,8 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ }' ``` - - #### Quick Start - Log to OTEL Collector @@ -491,7 +490,6 @@ litellm_settings: callbacks: ["otel"] ``` - **Step 2**: Start the proxy, make a test request Start proxy @@ -518,7 +516,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ - #### Quick Start - Log to OTEL GRPC Collector @@ -540,7 +537,6 @@ litellm_settings: callbacks: ["otel"] ``` - **Step 2**: Start the proxy, make a test request Start proxy @@ -565,7 +561,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ }' ``` - @@ -588,7 +583,6 @@ environment_variables: TRACELOOP_API_KEY: "XXXXX" ``` - **Step 3**: Start the proxy, make a test request Start proxy @@ -624,11 +618,15 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ❓ Use this when you want to **pass information about the incoming request in a distributed tracing system** ✅ Key change: Pass the **`traceparent` header** in your requests. [Read more about traceparent headers here](https://uptrace.dev/opentelemetry/opentelemetry-traceparent.html#what-is-traceparent-header) + ```curl traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01 ``` + Example Usage + 1. Make Request to LiteLLM Proxy with `traceparent` header + ```python import openai import uuid @@ -652,7 +650,6 @@ response = client.chat.completions.create( ) print(response) - ``` ```shell @@ -666,12 +663,12 @@ Search for Trace=`80e1afed08e019fc1110464cfa66635c` on your OTEL Collector - - ## Custom Callback Class [Async] + Use this when you want to run custom callbacks in `python` #### Step 1 - Create your custom `litellm` callback class + We use `litellm.integrations.custom_logger` for this, **more details about litellm custom callbacks [here](https://docs.litellm.ai/docs/observability/custom_callback)** Define your custom callback class in a python file. @@ -774,16 +771,17 @@ proxy_handler_instance = MyCustomHandler() ``` #### Step 2 - Pass your custom callback class in `config.yaml` + We pass the custom callback class defined in **Step1** to the config.yaml. Set `callbacks` to `python_filename.logger_instance_name` In the config below, we pass + - python_filename: `custom_callbacks.py` - logger_instance_name: `proxy_handler_instance`. This is defined in Step 1 `callbacks: custom_callbacks.proxy_handler_instance` - ```yaml model_list: - model_name: gpt-3.5-turbo @@ -796,6 +794,7 @@ litellm_settings: ``` #### Step 3 - Start proxy + test request + ```shell litellm --config proxy_config.yaml ``` @@ -817,6 +816,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ``` #### Resulting Log on Proxy + ```shell On Success Model: gpt-3.5-turbo, @@ -869,7 +869,6 @@ class MyCustomHandler(CustomLogger): "max_tokens": 10 } } - ``` #### Logging `model_info` set in config.yaml @@ -887,11 +886,13 @@ class MyCustomHandler(CustomLogger): ``` **Expected Output** + ```json {'mode': 'embedding', 'input_cost_per_token': 0.002} ``` ### Logging responses from proxy + Both `/chat/completions` and `/embeddings` responses are available as `response_obj` **Note: for `/chat/completions`, both `stream=True` and `non stream` responses are available as `response_obj`** @@ -905,6 +906,7 @@ class MyCustomHandler(CustomLogger): ``` **Expected Output /chat/completion [for both `stream` and `non-stream` responses]** + ```json ModelResponse( id='chatcmpl-8Tfu8GoMElwOZuj2JlHBhNHG01PPo', @@ -931,6 +933,7 @@ ModelResponse( ``` **Expected Output /embeddings** + ```json { 'model': 'ada', @@ -950,7 +953,6 @@ ModelResponse( } ``` - ## Custom Callback APIs [Async] :::info @@ -960,10 +962,12 @@ This is an Enterprise only feature [Get Started with Enterprise here](https://gi ::: Use this if you: + - Want to use custom callbacks written in a non Python programming language - Want your callbacks to run on a different microservice #### Step 1. Create your generic logging API endpoint + Set up a generic API endpoint that can receive data in JSON format. The data will be included within a "data" field. Your server should support the following Request format: @@ -1026,11 +1030,8 @@ async def log_event(request: Request): if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=4000) - - ``` - #### Step 2. Set your `GENERIC_LOGGER_ENDPOINT` to the endpoint + route we should send callback logs to ```shell @@ -1040,6 +1041,7 @@ os.environ["GENERIC_LOGGER_ENDPOINT"] = "http://localhost:4000/log-event" #### Step 3. Create a `config.yaml` file and set `litellm_settings`: `success_callback` = ["generic"] Example litellm proxy config.yaml + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1051,8 +1053,8 @@ litellm_settings: Start the LiteLLM Proxy and make a test request to verify the logs reached your callback API - ## Logging LLM IO to Galileo + [BETA] Log LLM I/O on [www.rungalileo.io](https://www.rungalileo.io/) @@ -1075,6 +1077,7 @@ export GALILEO_PASSWORD="" ### Quick Start 1. Add to Config.yaml + ```yaml model_list: - litellm_params: @@ -1110,7 +1113,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ' ``` - 🎉 That's it - Expect to see your Logs on your Galileo Dashboard ## Logging Proxy Cost + Usage - OpenMeter @@ -1128,6 +1130,7 @@ export OPENMETER_API_KEY="" ### Quick Start 1. Add to Config.yaml + ```yaml model_list: - litellm_params: @@ -1163,13 +1166,14 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ' ``` - ## Logging Proxy Input/Output - DataDog + We will use the `--config` to set `litellm.success_callback = ["datadog"]` this will log all successfull LLM calls to DataDog **Step 1**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1189,6 +1193,7 @@ DD_SITE="us5.datadoghq.com" # your datadog base url **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` @@ -1216,10 +1221,10 @@ Expected output on Datadog - ## Logging Proxy Input/Output - s3 Buckets We will use the `--config` to set + - `litellm.success_callback = ["s3"]` This will log all successfull LLM calls to s3 Bucket @@ -1233,6 +1238,7 @@ AWS_REGION_NAME = "" ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1252,11 +1258,13 @@ litellm_settings: **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ```shell curl --location 'http://0.0.0.0:4000/chat/completions' \ --header 'Content-Type: application/json' \ @@ -1276,6 +1284,7 @@ Your logs should be available on the specified s3 Bucket ## Logging Proxy Input/Output - DynamoDB We will use the `--config` to set + - `litellm.success_callback = ["dynamodb"]` - `litellm.dynamodb_table_name = "your-table-name"` @@ -1290,6 +1299,7 @@ AWS_REGION_NAME = "" ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1303,11 +1313,13 @@ litellm_settings: **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ```shell curl --location 'http://0.0.0.0:4000/chat/completions' \ --header 'Content-Type: application/json' \ @@ -1395,19 +1407,18 @@ Your logs should be available on DynamoDB } ``` - - - ## Logging Proxy Input/Output - Sentry If api calls fail (llm/database) you can log those to Sentry: **Step 1** Install Sentry + ```shell pip install --upgrade sentry-sdk ``` **Step 2**: Save your Sentry_DSN and add `litellm_settings`: `failure_callback` + ```shell export SENTRY_DSN="your-sentry-dsn" ``` @@ -1427,11 +1438,13 @@ general_settings: **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ``` litellm --test ``` @@ -1449,6 +1462,7 @@ ATHINA_API_KEY = "your-athina-api-key" ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1461,11 +1475,13 @@ litellm_settings: **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ``` curl --location 'http://0.0.0.0:4000/chat/completions' \ --header 'Content-Type: application/json' \ @@ -1497,6 +1513,7 @@ AZURE_CONTENT_SAFETY_KEY = "" ``` **Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` + ```yaml model_list: - model_name: gpt-3.5-turbo @@ -1512,11 +1529,13 @@ litellm_settings: **Step 3**: Start the proxy, make a test request Start proxy + ```shell litellm --config config.yaml --debug ``` Test Request + ``` curl --location 'http://0.0.0.0:4000/chat/completions' \ --header 'Content-Type: application/json' \ @@ -1532,7 +1551,8 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ``` An HTTP 400 error will be returned if the content is detected with a value greater than the threshold set in the `config.yaml`. -The details of the response will describe : +The details of the response will describe: + - The `source` : input text or llm generated text - The `category` : the category of the content that triggered the moderation - The `severity` : the severity from 0 to 10 From d0a7983a41d1007bbcf1a9db9902c33031d77943 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 17:22:14 -0700 Subject: [PATCH 30/89] fix try / except langfuse deep copy --- litellm/integrations/langfuse.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index e7b2f5e0bc3..a3fa1e2cee6 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -326,7 +326,12 @@ class LangFuseLogger: or isinstance(value, int) or isinstance(value, float) ): - new_metadata[key] = copy.deepcopy(value) + try: + new_metadata[key] = copy.deepcopy(value) + except Exception as e: + verbose_logger.error( + f"Langfuse [Non-blocking error] - error copying metadata: {str(e)}" + ) metadata = new_metadata supports_tags = Version(langfuse.version.__version__) >= Version("2.6.3") From 9590d63a384290663eeb7aea0a991f68aee07656 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 17:40:30 -0700 Subject: [PATCH 31/89] docs - Security Measures --- docs/my-website/docs/data_security.md | 13 +++++++++++++ docs/my-website/docs/proxy_server.md | 2 +- docs/my-website/sidebars.js | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/my-website/docs/data_security.md diff --git a/docs/my-website/docs/data_security.md b/docs/my-website/docs/data_security.md new file mode 100644 index 00000000000..2822c696c17 --- /dev/null +++ b/docs/my-website/docs/data_security.md @@ -0,0 +1,13 @@ +# Data Privacy and Security + +## Security Measures + +### Cloud LiteLLM + +- We encrypt all data stored using your `LITELLM_MASTER_KEY` and in transit using TLS. +- Our database and application run on GCP, AWS infrastructure, partly managed by NeonDB. + - US data region: Northern California (AWS/GCP `us-west-1`) & Virginia (AWS `us-east-1`) + - EU data region Germany/Frankfurt (AWS/GCP `eu-central-1`) +- All users have access to SSO (Single Sign-On) through OAuth 2.0 with Google, Okta, Microsoft, KeyCloak. + +For security inquiries, please contact us at support@berri.ai \ No newline at end of file diff --git a/docs/my-website/docs/proxy_server.md b/docs/my-website/docs/proxy_server.md index 87d30e16d6b..ef9352ab1f7 100644 --- a/docs/my-website/docs/proxy_server.md +++ b/docs/my-website/docs/proxy_server.md @@ -1,7 +1,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# [OLD PROXY 👉 [**NEW** proxy here](./simple_proxy)] Local OpenAI Proxy Server +# [OLD PROXY 👉 [NEW proxy here](./simple_proxy)] Local OpenAI Proxy Server A fast, and lightweight OpenAI-compatible server to call 100+ LLM APIs. diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 43f6eb7c2fc..6c53044a29f 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -237,6 +237,7 @@ const sidebars = { label: "Extras", items: [ "extras/contributing", + "data_security", "contributing", "rules", "proxy_server", From ca76d2fd72edd67caba9c47154d13fc7d69c0737 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 17:42:40 -0700 Subject: [PATCH 32/89] fix test_completion_bedrock_httpx_models --- litellm/tests/test_completion.py | 46 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index fdcc4f101af..011942a0f66 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -3065,32 +3065,38 @@ def response_format_tests(response: litellm.ModelResponse): @pytest.mark.asyncio async def test_completion_bedrock_httpx_models(sync_mode, model): litellm.set_verbose = True + try: - if sync_mode: - response = completion( - model=model, - messages=[{"role": "user", "content": "Hey! how's it going?"}], - temperature=0.2, - max_tokens=200, - ) + if sync_mode: + response = completion( + model=model, + messages=[{"role": "user", "content": "Hey! how's it going?"}], + temperature=0.2, + max_tokens=200, + ) - assert isinstance(response, litellm.ModelResponse) + assert isinstance(response, litellm.ModelResponse) - response_format_tests(response=response) - else: - response = await litellm.acompletion( - model=model, - messages=[{"role": "user", "content": "Hey! how's it going?"}], - temperature=0.2, - max_tokens=100, - ) + response_format_tests(response=response) + else: + response = await litellm.acompletion( + model=model, + messages=[{"role": "user", "content": "Hey! how's it going?"}], + temperature=0.2, + max_tokens=100, + ) - assert isinstance(response, litellm.ModelResponse) + assert isinstance(response, litellm.ModelResponse) + + print(f"response: {response}") + response_format_tests(response=response) print(f"response: {response}") - response_format_tests(response=response) - - print(f"response: {response}") + except litellm.RateLimitError as e: + print("got rate limit error=", e) + pass + except Exception as e: + pytest.fail(f"An error occurred - {str(e)}") def test_completion_bedrock_titan_null_response(): From da73f3abda75f728c9eebc8e078544e87d6888d2 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 17:54:54 -0700 Subject: [PATCH 33/89] docs security cloud litellm --- docs/my-website/docs/data_security.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/data_security.md b/docs/my-website/docs/data_security.md index 2822c696c17..b2d32b6e539 100644 --- a/docs/my-website/docs/data_security.md +++ b/docs/my-website/docs/data_security.md @@ -2,12 +2,33 @@ ## Security Measures -### Cloud LiteLLM +### LiteLLM Cloud - We encrypt all data stored using your `LITELLM_MASTER_KEY` and in transit using TLS. - Our database and application run on GCP, AWS infrastructure, partly managed by NeonDB. - US data region: Northern California (AWS/GCP `us-west-1`) & Virginia (AWS `us-east-1`) - EU data region Germany/Frankfurt (AWS/GCP `eu-central-1`) - All users have access to SSO (Single Sign-On) through OAuth 2.0 with Google, Okta, Microsoft, KeyCloak. +- Audit Logs with retention policy +- Control Allowed IP Addresses that can access your Cloud LiteLLM Instance -For security inquiries, please contact us at support@berri.ai \ No newline at end of file +For security inquiries, please contact us at support@berri.ai + +### Supported data regions for LiteLLM Cloud + +LiteLLM supports the following data regions: + +- US, Northern California (AWS/GCP `us-west-1`) +- Europe, Frankfurt, Germany (AWS/GCP `eu-central-1`) + +All data, user accounts, and infrastructure are completely separated between these two regions + +### Security Vulnerability Reporting Guidelines + +We value the security community's role in protecting our systems and users. To report a security vulnerability: + +- Email support@berri.ai with details +- Include steps to reproduce the issue +- Provide any relevant additional information + +We'll review all reports promptly. Note that we don't currently offer a bug bounty program. From b4a96e81c65e0349f25b29d5e34db5edf4838ec8 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 18:00:16 -0700 Subject: [PATCH 34/89] docs gdpr regions cloud --- docs/my-website/docs/hosted.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/my-website/docs/hosted.md b/docs/my-website/docs/hosted.md index 92940e8585f..99bfe990315 100644 --- a/docs/my-website/docs/hosted.md +++ b/docs/my-website/docs/hosted.md @@ -21,6 +21,14 @@ See our status page for [**live reliability**](https://status.litellm.ai/) - **Reliable**: Our hosted proxy is tested on 1k requests per second, making it reliable for high load. - **Secure**: LiteLLM is currently undergoing SOC-2 compliance, to make sure your data is as secure as possible. +## Data Privacy & Security + +You can find our [data privacy & security policy for cloud litellm here](../docs/data_security#litellm-cloud) + +## Supported data regions for LiteLLM Cloud + +You can find [supported data regions litellm here](../docs/data_security#supported-data-regions-for-litellm-cloud) + ### Pricing Pricing is based on usage. We can figure out a price that works for your team, on the call. From dc5adbb0ca420d47691e9c1870020b5e85719ed6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 18:03:54 -0700 Subject: [PATCH 35/89] docs - control allowed ip address --- docs/my-website/docs/enterprise.md | 1 + docs/my-website/docs/proxy/enterprise.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/my-website/docs/enterprise.md b/docs/my-website/docs/enterprise.md index 7035b25ce39..f33e2dda980 100644 --- a/docs/my-website/docs/enterprise.md +++ b/docs/my-website/docs/enterprise.md @@ -24,6 +24,7 @@ This covers: - ✅ [JWT-Auth](../docs/proxy/token_auth.md) - ✅ [Control available public, private routes](./proxy/enterprise#control-available-public-private-routes) - ✅ [[BETA] AWS Key Manager v2 - Key Decryption](./proxy/enterprise#beta-aws-key-manager---key-decryption) + - ✅ IP address‑based access control lists - ✅ Track Request IP Address - ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](./proxy/pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints) - ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](./proxy/enterprise#enforce-required-params-for-llm-requests) diff --git a/docs/my-website/docs/proxy/enterprise.md b/docs/my-website/docs/proxy/enterprise.md index 8aaf36c3e9c..f30b4b9781d 100644 --- a/docs/my-website/docs/proxy/enterprise.md +++ b/docs/my-website/docs/proxy/enterprise.md @@ -18,6 +18,7 @@ Features: - ✅ [JWT-Auth](../docs/proxy/token_auth.md) - ✅ [Control available public, private routes](#control-available-public-private-routes) - ✅ [[BETA] AWS Key Manager v2 - Key Decryption](#beta-aws-key-manager---key-decryption) + - ✅ IP address‑based access control lists - ✅ Track Request IP Address - ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints) - ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](#enforce-required-params-for-llm-requests) From f49837df19e129df90108ec740a7c7b45a70ac61 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 18:06:09 -0700 Subject: [PATCH 36/89] ci/cd run again --- litellm/tests/test_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 011942a0f66..7cee4a0d1e0 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -23,7 +23,7 @@ from litellm import RateLimitError, Timeout, completion, completion_cost, embedd from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.llms.prompt_templates.factory import anthropic_messages_pt -# litellm.num_retries = 3 +# litellm.num_retries=3 litellm.cache = None litellm.success_callback = [] user_message = "Write a short poem about the sky" From 2f8dbbeb97b9716c6b532a3360c0a89515f7a86b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 18:15:38 -0700 Subject: [PATCH 37/89] feat(proxy_server.py): working `/v1/messages` endpoint Works with claude engineer --- litellm/adapters/anthropic_adapter.py | 15 +- litellm/integrations/custom_logger.py | 7 +- litellm/llms/anthropic.py | 207 +++++++++------------ litellm/main.py | 3 +- litellm/proxy/auth/user_api_key_auth.py | 11 ++ litellm/proxy/proxy_server.py | 42 ++++- litellm/tests/test_anthropic_completion.py | 43 ++++- litellm/types/llms/anthropic.py | 48 +++++ litellm/types/utils.py | 48 +++-- 9 files changed, 272 insertions(+), 152 deletions(-) diff --git a/litellm/adapters/anthropic_adapter.py b/litellm/adapters/anthropic_adapter.py index ce75755ca00..7d9d799b665 100644 --- a/litellm/adapters/anthropic_adapter.py +++ b/litellm/adapters/anthropic_adapter.py @@ -8,11 +8,12 @@ from typing import Literal, Optional import dotenv import httpx +from pydantic import BaseModel import litellm from litellm import ChatCompletionRequest, verbose_logger from litellm.integrations.custom_logger import CustomLogger -from litellm.types.llms.anthropic import AnthropicMessagesRequest +from litellm.types.llms.anthropic import AnthropicMessagesRequest, AnthropicResponse class AnthropicAdapter(CustomLogger): @@ -31,12 +32,18 @@ class AnthropicAdapter(CustomLogger): translated_body = litellm.AnthropicConfig().translate_anthropic_to_openai( anthropic_message_request=request_body ) + return translated_body - def translate_completion_output_params(self, response: litellm.ModelResponse): - return super().translate_completion_output_params(response) + def translate_completion_output_params( + self, response: litellm.ModelResponse + ) -> Optional[AnthropicResponse]: - def translate_completion_output_params_streaming(self): + return litellm.AnthropicConfig().translate_openai_response_to_anthropic( + response=response + ) + + def translate_completion_output_params_streaming(self) -> Optional[BaseModel]: return super().translate_completion_output_params_streaming() diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py index 4c3fa3a1378..be02637041d 100644 --- a/litellm/integrations/custom_logger.py +++ b/litellm/integrations/custom_logger.py @@ -5,6 +5,7 @@ import traceback from typing import Literal, Optional, Union import dotenv +from pydantic import BaseModel from litellm.caching import DualCache from litellm.proxy._types import UserAPIKeyAuth @@ -67,13 +68,15 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac """ pass - def translate_completion_output_params(self, response: ModelResponse): + def translate_completion_output_params( + self, response: ModelResponse + ) -> Optional[BaseModel]: """ Translates the output params, from the OpenAI format to the custom format. """ pass - def translate_completion_output_params_streaming(self): + def translate_completion_output_params_streaming(self) -> Optional[BaseModel]: """ Translates the streaming chunk, from the OpenAI format to the custom format. """ diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index 02e222b9059..e4d6338ef7b 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -21,10 +21,15 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.types.llms.anthropic import ( AnthopicMessagesAssistantMessageParam, + AnthropicFinishReason, AnthropicMessagesRequest, AnthropicMessagesTool, AnthropicMessagesToolChoice, AnthropicMessagesUserMessageParam, + AnthropicResponse, + AnthropicResponseContentBlockText, + AnthropicResponseContentBlockToolUse, + AnthropicResponseUsageBlock, ContentBlockDelta, ContentBlockStart, MessageBlockDelta, @@ -51,7 +56,7 @@ from litellm.types.llms.openai import ( ChatCompletionUsageBlock, ChatCompletionUserMessage, ) -from litellm.types.utils import GenericStreamingChunk +from litellm.types.utils import Choices, GenericStreamingChunk from litellm.utils import CustomStreamWrapper, ModelResponse, Usage from .base import BaseLLM @@ -187,6 +192,8 @@ class AnthropicConfig: optional_params["top_p"] = value return optional_params + ### FOR [BETA] `/v1/messages` endpoint support + def translatable_anthropic_params(self) -> List: """ Which anthropic params, we need to translate to the openai format. @@ -300,10 +307,14 @@ class AnthropicConfig: ) ) - assistant_message = ChatCompletionAssistantMessage( - role="assistant", content=assistant_message_str, tool_calls=tool_calls - ) - new_messages.append(assistant_message) + if assistant_message_str is not None or len(tool_calls) > 0: + assistant_message = ChatCompletionAssistantMessage( + role="assistant", + content=assistant_message_str, + ) + if len(tool_calls) > 0: + assistant_message["tool_calls"] = tool_calls + new_messages.append(assistant_message) return new_messages @@ -391,6 +402,77 @@ class AnthropicConfig: return new_kwargs + def _translate_openai_content_to_anthropic( + self, choices: List[Choices] + ) -> List[ + Union[AnthropicResponseContentBlockText, AnthropicResponseContentBlockToolUse] + ]: + new_content: List[ + Union[ + AnthropicResponseContentBlockText, AnthropicResponseContentBlockToolUse + ] + ] = [] + for choice in choices: + if ( + choice.message.tool_calls is not None + and len(choice.message.tool_calls) > 0 + ): + for tool_call in choice.message.tool_calls: + new_content.append( + AnthropicResponseContentBlockToolUse( + type="tool_use", + id=tool_call.id, + name=tool_call.function.name, + input=tool_call.function.arguments, + ) + ) + elif choice.message.content is not None: + new_content.append( + AnthropicResponseContentBlockText( + type="text", text=choice.message.content + ) + ) + + return new_content + + def _translate_openai_finish_reason_to_anthropic( + self, openai_finish_reason: str + ) -> AnthropicFinishReason: + if openai_finish_reason == "stop": + return "end_turn" + elif openai_finish_reason == "length": + return "max_tokens" + elif openai_finish_reason == "tool_calls": + return "tool_use" + return "end_turn" + + def translate_openai_response_to_anthropic( + self, response: litellm.ModelResponse + ) -> AnthropicResponse: + ## translate content block + anthropic_content = self._translate_openai_content_to_anthropic(choices=response.choices) # type: ignore + ## extract finish reason + anthropic_finish_reason = self._translate_openai_finish_reason_to_anthropic( + openai_finish_reason=response.choices[0].finish_reason # type: ignore + ) + # extract usage + usage: litellm.Usage = getattr(response, "usage") + anthropic_usage = AnthropicResponseUsageBlock( + input_tokens=usage.prompt_tokens, output_tokens=usage.completion_tokens + ) + translated_obj = AnthropicResponse( + id=response.id, + type="message", + role="assistant", + model=response.model or "unknown-model", + stop_sequence=None, + usage=anthropic_usage, + content=anthropic_content, + stop_reason=anthropic_finish_reason, + ) + + return translated_obj + # makes headers for API call def validate_environment(api_key, user_headers): @@ -454,121 +536,6 @@ class AnthropicChatCompletion(BaseLLM): def __init__(self) -> None: super().__init__() - # def process_streaming_response( - # self, - # model: str, - # response: Union[requests.Response, httpx.Response], - # model_response: ModelResponse, - # stream: bool, - # logging_obj: litellm.litellm_core_utils.litellm_logging.Logging, - # optional_params: dict, - # api_key: str, - # data: Union[dict, str], - # messages: List, - # print_verbose, - # encoding, - # ) -> CustomStreamWrapper: - # """ - # Return stream object for tool-calling + streaming - # """ - # ## LOGGING - # logging_obj.post_call( - # input=messages, - # api_key=api_key, - # original_response=response.text, - # additional_args={"complete_input_dict": data}, - # ) - # print_verbose(f"raw model_response: {response.text}") - # ## RESPONSE OBJECT - # try: - # completion_response = response.json() - # except: - # raise AnthropicError( - # message=response.text, status_code=response.status_code - # ) - # text_content = "" - # tool_calls = [] - # for content in completion_response["content"]: - # if content["type"] == "text": - # text_content += content["text"] - # ## TOOL CALLING - # elif content["type"] == "tool_use": - # tool_calls.append( - # { - # "id": content["id"], - # "type": "function", - # "function": { - # "name": content["name"], - # "arguments": json.dumps(content["input"]), - # }, - # } - # ) - # if "error" in completion_response: - # raise AnthropicError( - # message=str(completion_response["error"]), - # status_code=response.status_code, - # ) - # _message = litellm.Message( - # tool_calls=tool_calls, - # content=text_content or None, - # ) - # model_response.choices[0].message = _message # type: ignore - # model_response._hidden_params["original_response"] = completion_response[ - # "content" - # ] # allow user to access raw anthropic tool calling response - - # model_response.choices[0].finish_reason = map_finish_reason( - # completion_response["stop_reason"] - # ) - - # print_verbose("INSIDE ANTHROPIC STREAMING TOOL CALLING CONDITION BLOCK") - # # return an iterator - # streaming_model_response = ModelResponse(stream=True) - # streaming_model_response.choices[0].finish_reason = model_response.choices[ # type: ignore - # 0 - # ].finish_reason - # # streaming_model_response.choices = [litellm.utils.StreamingChoices()] - # streaming_choice = litellm.utils.StreamingChoices() - # streaming_choice.index = model_response.choices[0].index - # _tool_calls = [] - # print_verbose( - # f"type of model_response.choices[0]: {type(model_response.choices[0])}" - # ) - # print_verbose(f"type of streaming_choice: {type(streaming_choice)}") - # if isinstance(model_response.choices[0], litellm.Choices): - # if getattr( - # model_response.choices[0].message, "tool_calls", None - # ) is not None and isinstance( - # model_response.choices[0].message.tool_calls, list - # ): - # for tool_call in model_response.choices[0].message.tool_calls: - # _tool_call = {**tool_call.dict(), "index": 0} - # _tool_calls.append(_tool_call) - # delta_obj = litellm.utils.Delta( - # content=getattr(model_response.choices[0].message, "content", None), - # role=model_response.choices[0].message.role, - # tool_calls=_tool_calls, - # ) - # streaming_choice.delta = delta_obj - # streaming_model_response.choices = [streaming_choice] - # completion_stream = ModelResponseIterator( - # model_response=streaming_model_response - # ) - # print_verbose( - # "Returns anthropic CustomStreamWrapper with 'cached_response' streaming object" - # ) - # return CustomStreamWrapper( - # completion_stream=completion_stream, - # model=model, - # custom_llm_provider="cached_response", - # logging_obj=logging_obj, - # ) - # else: - # raise AnthropicError( - # status_code=422, - # message="Unprocessable response object - {}".format(response.text), - # ) - def process_response( self, model: str, diff --git a/litellm/main.py b/litellm/main.py index bb203ae4a96..74a86e1e5d6 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -38,6 +38,7 @@ import dotenv import httpx import openai import tiktoken +from pydantic import BaseModel from typing_extensions import overload import litellm @@ -3947,7 +3948,7 @@ def text_completion( ###### Adapter Completion ################ -def adapter_completion(*, adapter_id: str, **kwargs) -> Any: +def adapter_completion(*, adapter_id: str, **kwargs) -> Optional[BaseModel]: translation_obj: Optional[CustomLogger] = None for item in litellm.adapters: if item["id"] == adapter_id: diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 28d67ad8cfc..4b931a27268 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -71,6 +71,11 @@ azure_api_key_header = APIKeyHeader( auto_error=False, description="Some older versions of the openai Python package will send an API-Key header with just the API key ", ) +anthropic_api_key_header = APIKeyHeader( + name="x-api-key", + auto_error=False, + description="If anthropic client used.", +) def _get_bearer_token( @@ -87,6 +92,9 @@ async def user_api_key_auth( request: Request, api_key: str = fastapi.Security(api_key_header), azure_api_key_header: str = fastapi.Security(azure_api_key_header), + anthropic_api_key_header: Optional[str] = fastapi.Security( + anthropic_api_key_header + ), ) -> UserAPIKeyAuth: from litellm.proxy.proxy_server import ( @@ -114,6 +122,9 @@ async def user_api_key_auth( elif isinstance(azure_api_key_header, str): api_key = azure_api_key_header + elif isinstance(anthropic_api_key_header, str): + api_key = anthropic_api_key_header + parent_otel_span: Optional[Span] = None if open_telemetry_logger is not None: parent_otel_span = open_telemetry_logger.tracer.start_span( diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 581cd9229d6..ea1fa8f7567 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -210,6 +210,12 @@ from litellm.router import ( from litellm.router import ModelInfo as RouterModelInfo from litellm.router import updateDeployment from litellm.scheduler import DefaultPriorities, FlowItem, Scheduler +from litellm.types.llms.anthropic import ( + AnthropicMessagesRequest, + AnthropicResponse, + AnthropicResponseContentBlockText, + AnthropicResponseUsageBlock, +) from litellm.types.llms.openai import HttpxBinaryResponseContent from litellm.types.router import RouterGeneralSettings @@ -5030,6 +5036,34 @@ async def moderations( ) +#### ANTHROPIC ENDPOINTS #### + + +@router.post( + "/v1/messages", + tags=["[beta] Anthropic `/v1/messages`"], + dependencies=[Depends(user_api_key_auth)], + response_model=AnthropicResponse, +) +async def anthropic_response(data: AnthropicMessagesRequest): + from litellm import adapter_completion + from litellm.adapters.anthropic_adapter import anthropic_adapter + + litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}] + + response: Optional[BaseModel] = adapter_completion(adapter_id="anthropic", **data) + + if response is None: + raise Exception("Response is None.") + elif not isinstance(response, AnthropicResponse): + raise Exception( + "Invalid model response={}. Not in 'AnthropicResponse' format".format( + response + ) + ) + return response + + #### DEV UTILS #### # @router.get( @@ -7546,7 +7580,7 @@ async def login(request: Request): litellm_dashboard_ui += "/ui/" import jwt - jwt_token = jwt.encode( + jwt_token = jwt.encode( # type: ignore { "user_id": user_id, "key": key, @@ -7610,7 +7644,7 @@ async def login(request: Request): litellm_dashboard_ui += "/ui/" import jwt - jwt_token = jwt.encode( + jwt_token = jwt.encode( # type: ignore { "user_id": user_id, "key": key, @@ -7745,7 +7779,7 @@ async def onboarding(invite_link: str): litellm_dashboard_ui += "/ui/onboarding" import jwt - jwt_token = jwt.encode( + jwt_token = jwt.encode( # type: ignore { "user_id": user_obj.user_id, "key": key, @@ -8162,7 +8196,7 @@ async def auth_callback(request: Request): import jwt - jwt_token = jwt.encode( + jwt_token = jwt.encode( # type: ignore { "user_id": user_id, "key": key, diff --git a/litellm/tests/test_anthropic_completion.py b/litellm/tests/test_anthropic_completion.py index 25d5823c324..4bd33e04c39 100644 --- a/litellm/tests/test_anthropic_completion.py +++ b/litellm/tests/test_anthropic_completion.py @@ -20,16 +20,51 @@ from unittest.mock import MagicMock, patch import pytest import litellm -from litellm import adapter_completion +from litellm import AnthropicConfig, adapter_completion from litellm.adapters.anthropic_adapter import anthropic_adapter +from litellm.types.llms.anthropic import AnthropicResponse -def test_anthropic_completion(): +def test_anthropic_completion_messages_translation(): + messages = [{"role": "user", "content": "Hey, how's it going?"}] + + translated_messages = AnthropicConfig().translate_anthropic_messages_to_openai(messages=messages) # type: ignore + + assert translated_messages == [{"role": "user", "content": "Hey, how's it going?"}] + + +def test_anthropic_completion_input_translation(): + data = { + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "Hey, how's it going?"}], + } + translated_input = anthropic_adapter.translate_completion_input_params(kwargs=data) + + assert translated_input is not None + + assert translated_input["model"] == "gpt-3.5-turbo" + assert translated_input["messages"] == [ + {"role": "user", "content": "Hey, how's it going?"} + ] + + +def test_anthropic_completion_e2e(): + litellm.set_verbose = True + litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}] messages = [{"role": "user", "content": "Hey, how's it going?"}] response = adapter_completion( - model="gpt-3.5-turbo", messages=messages, adapter_id="anthropic" + model="gpt-3.5-turbo", + messages=messages, + adapter_id="anthropic", + mock_response="This is a fake call", ) - print(response) + print("Response: {}".format(response)) + + assert response is not None + + assert isinstance(response, AnthropicResponse) + + assert False diff --git a/litellm/types/llms/anthropic.py b/litellm/types/llms/anthropic.py index 7df73377d03..00cad587ca2 100644 --- a/litellm/types/llms/anthropic.py +++ b/litellm/types/llms/anthropic.py @@ -223,3 +223,51 @@ class MessageStartBlock(TypedDict): type: Literal["message_start"] message: MessageChunk + + +class AnthropicResponseContentBlockText(BaseModel): + type: Literal["text"] + text: str + + +class AnthropicResponseContentBlockToolUse(BaseModel): + type: Literal["tool_use"] + id: str + name: str + input: str + + +class AnthropicResponseUsageBlock(BaseModel): + input_tokens: int + output_tokens: int + + +AnthropicFinishReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] + + +class AnthropicResponse(BaseModel): + id: str + """Unique object identifier.""" + + type: Literal["message"] + """For Messages, this is always "message".""" + + role: Literal["assistant"] + """Conversational role of the generated message. This will always be "assistant".""" + + content: List[ + Union[AnthropicResponseContentBlockText, AnthropicResponseContentBlockToolUse] + ] + """Content generated by the model.""" + + model: str + """The model that handled the request.""" + + stop_reason: Optional[AnthropicFinishReason] + """The reason that we stopped.""" + + stop_sequence: Optional[str] + """Which custom stop sequence was generated, if any.""" + + usage: AnthropicResponseUsageBlock + """Billing and rate-limit usage.""" diff --git a/litellm/types/utils.py b/litellm/types/utils.py index a90f93484f1..5f31798da9c 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -166,7 +166,7 @@ class FunctionCall(OpenAIObject): class Function(OpenAIObject): arguments: str - name: Optional[str] = None + name: str def __init__( self, @@ -280,29 +280,43 @@ class ChatCompletionMessageToolCall(OpenAIObject): setattr(self, key, value) +""" +Reference: +ChatCompletionMessage(content='This is a test', role='assistant', function_call=None, tool_calls=None)) +""" + + class Message(OpenAIObject): + + content: Optional[str] + role: Literal["assistant"] + tool_calls: Optional[List[ChatCompletionMessageToolCall]] + function_call: Optional[FunctionCall] + def __init__( self, - content: Optional[str] = "default", - role="assistant", - logprobs=None, + content: Optional[str] = None, + role: Literal["assistant"] = "assistant", function_call=None, tool_calls=None, **params, ): - super(Message, self).__init__(**params) - self.content = content - self.role = role - if function_call is not None: - self.function_call = FunctionCall(**function_call) - - if tool_calls is not None: - self.tool_calls = [] - for tool_call in tool_calls: - self.tool_calls.append(ChatCompletionMessageToolCall(**tool_call)) - - if logprobs is not None: - self._logprobs = ChoiceLogprobs(**logprobs) + init_values = { + "content": content, + "role": role, + "function_call": ( + FunctionCall(**function_call) if function_call is not None else None + ), + "tool_calls": ( + [ChatCompletionMessageToolCall(**tool_call) for tool_call in tool_calls] + if tool_calls is not None + else None + ), + } + super(Message, self).__init__( + **init_values, + **params, + ) def get(self, key, default=None): # Custom .get() method to access attributes with a default value if the attribute doesn't exist From b2f4cd9b567259196e3e485ade9621151eb1d525 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 10 Jul 2024 18:34:30 -0700 Subject: [PATCH 38/89] =?UTF-8?q?bump:=20version=201.41.15=20=E2=86=92=201?= =?UTF-8?q?.41.16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8bac85210da..1a877cf9b59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.41.15" +version = "1.41.16" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -91,7 +91,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.41.15" +version = "1.41.16" version_files = [ "pyproject.toml:^version" ] From 3d86c4f515b5c50f4a4045ad30bd1d99441fa9d3 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 18:40:08 -0700 Subject: [PATCH 39/89] Shorter success callbacks from /health/readiness Before: ```shell $ curl -sSL http://0.0.0.0:4000/health/readiness | jq '.success_callbacks' [ "langfuse", "", ">", "", "", "", "" ] ``` After: ```shell $ curl -sSL http://0.0.0.0:4000/health/readiness | jq '.success_callbacks' [ "langfuse", "_PROXY_track_cost_callback", "response_taking_too_long_callback", "_PROXY_MaxParallelRequestsHandler", "_PROXY_MaxBudgetLimiter", "_PROXY_CacheControlCheck", "ServiceLogging" ] ``` --- .../proxy/health_endpoints/_health_endpoints.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 29ad6bae7c7..e5ba03aacda 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -406,6 +406,19 @@ async def active_callbacks(): } +def callback_name(callback): + if isinstance(callback, str): + return callback + + try: + return callback.__name__ + except AttributeError: + try: + return callback.__class__.__name__ + except AttributeError: + return str(callback) + + @router.get( "/health/readiness", tags=["health"], @@ -424,8 +437,8 @@ async def health_readiness(): try: # this was returning a JSON of the values in some of the callbacks # all we need is the callback name, hence we do str(callback) - success_callback_names = [str(x) for x in litellm.success_callback] - except: + success_callback_names = [callback_name(x) for x in litellm.success_callback] + except AttributeError: # don't let this block the /health/readiness response, if we can't convert to str -> return litellm.success_callback success_callback_names = litellm.success_callback From 31829855c02bdc60195d67ed64f61dcbe98ed90c Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 18:53:54 -0700 Subject: [PATCH 40/89] feat(proxy_server.py): working `/v1/messages` with config.yaml Adds async router support for adapter_completion call --- litellm/llms/anthropic.py | 2 +- litellm/main.py | 30 ++++ litellm/proxy/_new_secret_config.yaml | 4 +- litellm/proxy/proxy_server.py | 182 ++++++++++++++++++++- litellm/router.py | 119 ++++++++++++++ litellm/tests/test_anthropic_completion.py | 37 ++++- litellm/types/llms/anthropic.py | 2 +- 7 files changed, 362 insertions(+), 14 deletions(-) diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index e4d6338ef7b..5fe527c694e 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -423,7 +423,7 @@ class AnthropicConfig: type="tool_use", id=tool_call.id, name=tool_call.function.name, - input=tool_call.function.arguments, + input=json.loads(tool_call.function.arguments), ) ) elif choice.message.content is not None: diff --git a/litellm/main.py b/litellm/main.py index 74a86e1e5d6..949466642ca 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -3948,6 +3948,36 @@ def text_completion( ###### Adapter Completion ################ +async def aadapter_completion(*, adapter_id: str, **kwargs) -> Optional[BaseModel]: + """ + Implemented to handle async calls for adapter_completion() + """ + try: + translation_obj: Optional[CustomLogger] = None + for item in litellm.adapters: + if item["id"] == adapter_id: + translation_obj = item["adapter"] + + if translation_obj is None: + raise ValueError( + "No matching adapter given. Received 'adapter_id'={}, litellm.adapters={}".format( + adapter_id, litellm.adapters + ) + ) + + new_kwargs = translation_obj.translate_completion_input_params(kwargs=kwargs) + + response: ModelResponse = await acompletion(**new_kwargs) # type: ignore + + translated_response = translation_obj.translate_completion_output_params( + response=response + ) + + return translated_response + except Exception as e: + raise e + + def adapter_completion(*, adapter_id: str, **kwargs) -> Optional[BaseModel]: translation_obj: Optional[CustomLogger] = None for item in litellm.adapters: diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 49db7c3787e..0f1f981d7ad 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -2,7 +2,9 @@ model_list: - model_name: "*" litellm_params: model: "openai/*" - + - model_name: claude-3-5-sonnet-20240620 + litellm_params: + model: gpt-3.5-turbo general_settings: diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index ea1fa8f7567..4bef33cbc47 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5045,23 +5045,187 @@ async def moderations( dependencies=[Depends(user_api_key_auth)], response_model=AnthropicResponse, ) -async def anthropic_response(data: AnthropicMessagesRequest): +async def anthropic_response( + anthropic_data: AnthropicMessagesRequest, + fastapi_response: Response, + request: Request, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): from litellm import adapter_completion from litellm.adapters.anthropic_adapter import anthropic_adapter litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}] - response: Optional[BaseModel] = adapter_completion(adapter_id="anthropic", **data) + global user_temperature, user_request_timeout, user_max_tokens, user_api_base + data: dict = {**anthropic_data, "adapter_id": "anthropic"} + try: + data["model"] = ( + general_settings.get("completion_model", None) # server default + or user_model # model name passed via cli args + or data["model"] # default passed in http request + ) + if user_model: + data["model"] = user_model - if response is None: - raise Exception("Response is None.") - elif not isinstance(response, AnthropicResponse): - raise Exception( - "Invalid model response={}. Not in 'AnthropicResponse' format".format( - response + data = await add_litellm_data_to_request( + data=data, # type: ignore + request=request, + general_settings=general_settings, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + ) + + # override with user settings, these are params passed via cli + if user_temperature: + data["temperature"] = user_temperature + if user_request_timeout: + data["request_timeout"] = user_request_timeout + if user_max_tokens: + data["max_tokens"] = user_max_tokens + if user_api_base: + data["api_base"] = user_api_base + + ### MODEL ALIAS MAPPING ### + # check if model name in model alias map + # get the actual model name + if data["model"] in litellm.model_alias_map: + data["model"] = litellm.model_alias_map[data["model"]] + + ### CALL HOOKS ### - modify incoming data before calling the model + data = await proxy_logging_obj.pre_call_hook( # type: ignore + user_api_key_dict=user_api_key_dict, data=data, call_type="text_completion" + ) + + ### ROUTE THE REQUESTs ### + router_model_names = llm_router.model_names if llm_router is not None else [] + # skip router if user passed their key + if "api_key" in data: + llm_response = asyncio.create_task(litellm.aadapter_completion(**data)) + elif ( + llm_router is not None and data["model"] in router_model_names + ): # model in router model list + llm_response = asyncio.create_task(llm_router.aadapter_completion(**data)) + elif ( + llm_router is not None + and llm_router.model_group_alias is not None + and data["model"] in llm_router.model_group_alias + ): # model set in model_group_alias + llm_response = asyncio.create_task(llm_router.aadapter_completion(**data)) + elif ( + llm_router is not None and data["model"] in llm_router.deployment_names + ): # model in router deployments, calling a specific deployment on the router + llm_response = asyncio.create_task( + llm_router.aadapter_completion(**data, specific_deployment=True) + ) + elif ( + llm_router is not None and data["model"] in llm_router.get_model_ids() + ): # model in router model list + llm_response = asyncio.create_task(llm_router.aadapter_completion(**data)) + elif ( + llm_router is not None + and data["model"] not in router_model_names + and llm_router.default_deployment is not None + ): # model in router deployments, calling a specific deployment on the router + llm_response = asyncio.create_task(llm_router.aadapter_completion(**data)) + elif user_model is not None: # `litellm --model ` + llm_response = asyncio.create_task(litellm.aadapter_completion(**data)) + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail={ + "error": "completion: Invalid model name passed in model=" + + data.get("model", "") + }, + ) + + # Await the llm_response task + response = await llm_response + + hidden_params = getattr(response, "_hidden_params", {}) or {} + model_id = hidden_params.get("model_id", None) or "" + cache_key = hidden_params.get("cache_key", None) or "" + api_base = hidden_params.get("api_base", None) or "" + response_cost = hidden_params.get("response_cost", None) or "" + + ### ALERTING ### + asyncio.create_task( + proxy_logging_obj.update_request_status( + litellm_call_id=data.get("litellm_call_id", ""), status="success" ) ) - return response + + verbose_proxy_logger.debug("final response: %s", response) + + fastapi_response.headers.update( + get_custom_headers( + user_api_key_dict=user_api_key_dict, + model_id=model_id, + cache_key=cache_key, + api_base=api_base, + version=version, + response_cost=response_cost, + ) + ) + + verbose_proxy_logger.info("\nResponse from Litellm:\n{}".format(response)) + return response + except RejectedRequestError as e: + _data = e.request_data + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, + original_exception=e, + request_data=_data, + ) + if _data.get("stream", None) is not None and _data["stream"] == True: + _chat_response = litellm.ModelResponse() + _usage = litellm.Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + ) + _chat_response.usage = _usage # type: ignore + _chat_response.choices[0].message.content = e.message # type: ignore + _iterator = litellm.utils.ModelResponseIterator( + model_response=_chat_response, convert_to_delta=True + ) + _streaming_response = litellm.TextCompletionStreamWrapper( + completion_stream=_iterator, + model=_data.get("model", ""), + ) + + selected_data_generator = select_data_generator( + response=_streaming_response, + user_api_key_dict=user_api_key_dict, + request_data=data, + ) + + return StreamingResponse( + selected_data_generator, + media_type="text/event-stream", + headers={}, + ) + else: + _response = litellm.TextCompletionResponse() + _response.choices[0].text = e.message + return _response + except Exception as e: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data + ) + verbose_proxy_logger.error( + "litellm.proxy.proxy_server.completion(): Exception occured - {}".format( + str(e) + ) + ) + verbose_proxy_logger.debug(traceback.format_exc()) + error_msg = f"{str(e)}" + raise ProxyException( + message=getattr(e, "message", error_msg), + type=getattr(e, "type", "None"), + param=getattr(e, "param", "None"), + code=getattr(e, "status_code", 500), + ) #### DEV UTILS #### diff --git a/litellm/router.py b/litellm/router.py index 944ae93779a..762f8c354cf 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1764,6 +1764,125 @@ class Router: self.fail_calls[model] += 1 raise e + async def aadapter_completion( + self, + adapter_id: str, + model: str, + is_retry: Optional[bool] = False, + is_fallback: Optional[bool] = False, + is_async: Optional[bool] = False, + **kwargs, + ): + try: + kwargs["model"] = model + kwargs["adapter_id"] = adapter_id + kwargs["original_function"] = self._aadapter_completion + kwargs["num_retries"] = kwargs.get("num_retries", self.num_retries) + timeout = kwargs.get("request_timeout", self.timeout) + kwargs.setdefault("metadata", {}).update({"model_group": model}) + response = await self.async_function_with_fallbacks(**kwargs) + + return response + except Exception as e: + asyncio.create_task( + send_llm_exception_alert( + litellm_router_instance=self, + request_kwargs=kwargs, + error_traceback_str=traceback.format_exc(), + original_exception=e, + ) + ) + raise e + + async def _aadapter_completion(self, adapter_id: str, model: str, **kwargs): + try: + verbose_router_logger.debug( + f"Inside _aadapter_completion()- model: {model}; kwargs: {kwargs}" + ) + deployment = await self.async_get_available_deployment( + model=model, + messages=[{"role": "user", "content": "default text"}], + specific_deployment=kwargs.pop("specific_deployment", None), + ) + kwargs.setdefault("metadata", {}).update( + { + "deployment": deployment["litellm_params"]["model"], + "model_info": deployment.get("model_info", {}), + "api_base": deployment.get("litellm_params", {}).get("api_base"), + } + ) + kwargs["model_info"] = deployment.get("model_info", {}) + data = deployment["litellm_params"].copy() + model_name = data["model"] + for k, v in self.default_litellm_params.items(): + if ( + k not in kwargs + ): # prioritize model-specific params > default router params + kwargs[k] = v + elif k == "metadata": + kwargs[k].update(v) + + potential_model_client = self._get_client( + deployment=deployment, kwargs=kwargs, client_type="async" + ) + # check if provided keys == client keys # + dynamic_api_key = kwargs.get("api_key", None) + if ( + dynamic_api_key is not None + and potential_model_client is not None + and dynamic_api_key != potential_model_client.api_key + ): + model_client = None + else: + model_client = potential_model_client + self.total_calls[model_name] += 1 + + response = litellm.aadapter_completion( + **{ + **data, + "adapter_id": adapter_id, + "caching": self.cache_responses, + "client": model_client, + "timeout": self.timeout, + **kwargs, + } + ) + + rpm_semaphore = self._get_client( + deployment=deployment, + kwargs=kwargs, + client_type="max_parallel_requests", + ) + + if rpm_semaphore is not None and isinstance( + rpm_semaphore, asyncio.Semaphore + ): + async with rpm_semaphore: + """ + - Check rpm limits before making the call + - If allowed, increment the rpm limit (allows global value to be updated, concurrency-safe) + """ + await self.async_routing_strategy_pre_call_checks( + deployment=deployment + ) + response = await response # type: ignore + else: + await self.async_routing_strategy_pre_call_checks(deployment=deployment) + response = await response # type: ignore + + self.success_calls[model_name] += 1 + verbose_router_logger.info( + f"litellm.aadapter_completion(model={model_name})\033[32m 200 OK\033[0m" + ) + return response + except Exception as e: + verbose_router_logger.info( + f"litellm.aadapter_completion(model={model})\033[31m Exception {str(e)}\033[0m" + ) + if model is not None: + self.fail_calls[model] += 1 + raise e + def embedding( self, model: str, diff --git a/litellm/tests/test_anthropic_completion.py b/litellm/tests/test_anthropic_completion.py index 4bd33e04c39..cac0945d8da 100644 --- a/litellm/tests/test_anthropic_completion.py +++ b/litellm/tests/test_anthropic_completion.py @@ -20,7 +20,7 @@ from unittest.mock import MagicMock, patch import pytest import litellm -from litellm import AnthropicConfig, adapter_completion +from litellm import AnthropicConfig, Router, adapter_completion from litellm.adapters.anthropic_adapter import anthropic_adapter from litellm.types.llms.anthropic import AnthropicResponse @@ -67,4 +67,37 @@ def test_anthropic_completion_e2e(): assert isinstance(response, AnthropicResponse) - assert False + +@pytest.mark.asyncio +async def test_anthropic_router_completion_e2e(): + litellm.set_verbose = True + + litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}] + + router = Router( + model_list=[ + { + "model_name": "claude-3-5-sonnet-20240620", + "litellm_params": { + "model": "gpt-3.5-turbo", + "mock_response": "hi this is macintosh.", + }, + } + ] + ) + messages = [{"role": "user", "content": "Hey, how's it going?"}] + + response = await router.aadapter_completion( + model="claude-3-5-sonnet-20240620", + messages=messages, + adapter_id="anthropic", + mock_response="This is a fake call", + ) + + print("Response: {}".format(response)) + + assert response is not None + + assert isinstance(response, AnthropicResponse) + + assert response.model == "gpt-3.5-turbo" diff --git a/litellm/types/llms/anthropic.py b/litellm/types/llms/anthropic.py index 00cad587ca2..33f413eced7 100644 --- a/litellm/types/llms/anthropic.py +++ b/litellm/types/llms/anthropic.py @@ -234,7 +234,7 @@ class AnthropicResponseContentBlockToolUse(BaseModel): type: Literal["tool_use"] id: str name: str - input: str + input: dict class AnthropicResponseUsageBlock(BaseModel): From 6ccd6352167dfee2517966a1ce7e6b954dc1e331 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 19:02:48 -0700 Subject: [PATCH 41/89] Update doc: health.md --- docs/my-website/docs/proxy/health.md | 34 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/my-website/docs/proxy/health.md b/docs/my-website/docs/proxy/health.md index c67302e0d0d..5a89265766e 100644 --- a/docs/my-website/docs/proxy/health.md +++ b/docs/my-website/docs/proxy/health.md @@ -128,9 +128,20 @@ Example Response: ```json { - "status": "healthy", - "db": "connected", - "litellm_version":"1.19.2", + "status": "connected", + "db": "connected", + "cache": null, + "litellm_version": "1.40.21", + "success_callbacks": [ + "langfuse", + "_PROXY_track_cost_callback", + "response_taking_too_long_callback", + "_PROXY_MaxParallelRequestsHandler", + "_PROXY_MaxBudgetLimiter", + "_PROXY_CacheControlCheck", + "ServiceLogging" + ], + "last_updated": "2024-07-10T18:59:10.616968" } ``` @@ -138,9 +149,20 @@ Example Response: ```json { - "status": "healthy", - "db": "Not connected", - "litellm_version":"1.19.2", + "status": "connected", + "db": "Not connected", + "cache": null, + "litellm_version": "1.40.21", + "success_callbacks": [ + "langfuse", + "_PROXY_track_cost_callback", + "response_taking_too_long_callback", + "_PROXY_MaxParallelRequestsHandler", + "_PROXY_MaxBudgetLimiter", + "_PROXY_CacheControlCheck", + "ServiceLogging" + ], + "last_updated": "2024-07-10T18:59:10.616968" } ``` From 434953b38c6ce92c1987f27d606e4d36a7f3f1b1 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 10 Jul 2024 19:08:48 -0700 Subject: [PATCH 42/89] More updates to health.md docs --- docs/my-website/docs/proxy/health.md | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/docs/my-website/docs/proxy/health.md b/docs/my-website/docs/proxy/health.md index 5a89265766e..0f39261130d 100644 --- a/docs/my-website/docs/proxy/health.md +++ b/docs/my-website/docs/proxy/health.md @@ -118,14 +118,12 @@ Unprotected endpoint for checking if proxy is ready to accept requests Example Request: -```bash -curl --location 'http://0.0.0.0:4000/health/readiness' +```bash +curl http://0.0.0.0:4000/health/readiness ``` Example Response: -*If proxy connected to a database* - ```json { "status": "connected", @@ -145,26 +143,8 @@ Example Response: } ``` -*If proxy not connected to a database* - -```json -{ - "status": "connected", - "db": "Not connected", - "cache": null, - "litellm_version": "1.40.21", - "success_callbacks": [ - "langfuse", - "_PROXY_track_cost_callback", - "response_taking_too_long_callback", - "_PROXY_MaxParallelRequestsHandler", - "_PROXY_MaxBudgetLimiter", - "_PROXY_CacheControlCheck", - "ServiceLogging" - ], - "last_updated": "2024-07-10T18:59:10.616968" -} -``` +If the proxy is not connected to a database, then the `"db"` field will be `"Not +connected"` instead of `"connected"` and the `"last_updated"` field will not be present. ## `/health/liveliness` From 1019355527a82f46f2701800a1fe53836da5ccbc Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 21:56:47 -0700 Subject: [PATCH 43/89] fix(types/utils.py): fix streaming function name --- litellm/types/utils.py | 4 +++- litellm/utils.py | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 5f31798da9c..4ae88a7450a 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -166,7 +166,9 @@ class FunctionCall(OpenAIObject): class Function(OpenAIObject): arguments: str - name: str + name: Optional[ + str + ] # can be None - openai e.g.: ChoiceDeltaToolCallFunction(arguments='{"', name=None), type=None) def __init__( self, diff --git a/litellm/utils.py b/litellm/utils.py index cf2c679a844..39ddc02ac9b 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -8126,7 +8126,7 @@ class CustomStreamWrapper: if chunk.startswith(self.complete_response): # Remove last_sent_chunk only if it appears at the start of the new chunk - chunk = chunk[len(self.complete_response):] + chunk = chunk[len(self.complete_response) :] self.complete_response += chunk return chunk @@ -9483,8 +9483,8 @@ class CustomStreamWrapper: model_response.choices[0].delta = Delta(**_json_delta) except Exception as e: verbose_logger.error( - "litellm.CustomStreamWrapper.chunk_creator(): Exception occured - {}".format( - str(e) + "litellm.CustomStreamWrapper.chunk_creator(): Exception occured - {}\n{}".format( + str(e), traceback.format_exc() ) ) verbose_logger.debug(traceback.format_exc()) @@ -10124,7 +10124,7 @@ def mock_completion_streaming_obj( model_response, mock_response, model, n: Optional[int] = None ): for i in range(0, len(mock_response), 3): - completion_obj = Delta(role="assistant", content=mock_response[i: i + 3]) + completion_obj = Delta(role="assistant", content=mock_response[i : i + 3]) if n is None: model_response.choices[0].delta = completion_obj else: @@ -10133,7 +10133,7 @@ def mock_completion_streaming_obj( _streaming_choice = litellm.utils.StreamingChoices( index=j, delta=litellm.utils.Delta( - role="assistant", content=mock_response[i: i + 3] + role="assistant", content=mock_response[i : i + 3] ), ) _all_choices.append(_streaming_choice) @@ -10145,7 +10145,7 @@ async def async_mock_completion_streaming_obj( model_response, mock_response, model, n: Optional[int] = None ): for i in range(0, len(mock_response), 3): - completion_obj = Delta(role="assistant", content=mock_response[i: i + 3]) + completion_obj = Delta(role="assistant", content=mock_response[i : i + 3]) if n is None: model_response.choices[0].delta = completion_obj else: @@ -10154,7 +10154,7 @@ async def async_mock_completion_streaming_obj( _streaming_choice = litellm.utils.StreamingChoices( index=j, delta=litellm.utils.Delta( - role="assistant", content=mock_response[i: i + 3] + role="assistant", content=mock_response[i : i + 3] ), ) _all_choices.append(_streaming_choice) From 22bcbac9eaef2b519fd32946303e72a086551316 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 22:14:23 -0700 Subject: [PATCH 44/89] fix: fix linting error --- litellm/llms/anthropic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index 5fe527c694e..ca93a85b71d 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -422,7 +422,7 @@ class AnthropicConfig: AnthropicResponseContentBlockToolUse( type="tool_use", id=tool_call.id, - name=tool_call.function.name, + name=tool_call.function.name or "", input=json.loads(tool_call.function.arguments), ) ) From 03e15d020e52801623022d53bcf855873680e681 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 22:43:26 -0700 Subject: [PATCH 45/89] =?UTF-8?q?bump:=20version=201.41.16=20=E2=86=92=201?= =?UTF-8?q?.41.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1a877cf9b59..9e69e340466 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.41.16" +version = "1.41.17" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -91,7 +91,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.41.16" +version = "1.41.17" version_files = [ "pyproject.toml:^version" ] From 4b0181c79c03b68864b18d4d5083a5cd925718fc Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 10 Jul 2024 22:56:33 -0700 Subject: [PATCH 46/89] docs(anthropic_completion.md): add doc on anthropic `/v1/messages` endpoint support --- docs/my-website/docs/anthropic_completion.md | 54 ++++++++++++++++++++ docs/my-website/sidebars.js | 1 + 2 files changed, 55 insertions(+) create mode 100644 docs/my-website/docs/anthropic_completion.md diff --git a/docs/my-website/docs/anthropic_completion.md b/docs/my-website/docs/anthropic_completion.md new file mode 100644 index 00000000000..ca65f3f6f5b --- /dev/null +++ b/docs/my-website/docs/anthropic_completion.md @@ -0,0 +1,54 @@ +# [BETA] Anthropic `/v1/messages` + +Call 100+ LLMs in the Anthropic format. + + +1. Setup config.yaml + +```yaml +model_list: + - model_name: my-test-model + litellm_params: + model: gpt-3.5-turbo +``` + +2. Start proxy + +```bash +litellm --config /path/to/config.yaml +``` + +3. Test it! + +```bash +curl -X POST 'http://0.0.0.0:4000/v1/messages' \ +-H 'x-api-key: sk-1234' \ +-H 'content-type: application/json' \ +-D '{ + "model": "my-test-model", + "max_tokens": 1024, + "messages": [ + {"role": "user", "content": "Hello, world"} + ] +}' +``` + +## Test with Anthropic SDK + +```python +import os +from anthropic import Anthropic + +client = Anthropic(api_key="sk-1234", base_url="http://0.0.0.0:4000") # 👈 CONNECT TO PROXY + +message = client.messages.create( + messages=[ + { + "role": "user", + "content": "Hello, Claude", + } + ], + model="my-test-model", # 👈 set 'model_name' +) +print(message.content) +``` \ No newline at end of file diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 6c53044a29f..68b4601ce39 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -117,6 +117,7 @@ const sidebars = { "text_to_speech", "assistants", "batches", + "anthropic_completion" ], }, { From 498d7d42289c2222a9318ce3706182843ad16a7c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 07:22:45 -0700 Subject: [PATCH 47/89] fix - otel log db / redis calls --- litellm/proxy/common_utils/init_callbacks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/litellm/proxy/common_utils/init_callbacks.py b/litellm/proxy/common_utils/init_callbacks.py index 9631bcad453..a54260d8d19 100644 --- a/litellm/proxy/common_utils/init_callbacks.py +++ b/litellm/proxy/common_utils/init_callbacks.py @@ -35,6 +35,10 @@ def initialize_callbacks_on_proxy( open_telemetry_logger = OpenTelemetry() + # Add Otel as a service callback + if "otel" not in litellm.service_callback: + litellm.service_callback.append("otel") + imported_list.append(open_telemetry_logger) setattr(proxy_server, "open_telemetry_logger", open_telemetry_logger) elif isinstance(callback, str) and callback == "presidio": From 19d993e120f5d937320e4aa5348425208c5f508e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 07:24:48 -0700 Subject: [PATCH 48/89] add otel in callbacks --- litellm/proxy/proxy_config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index e580b3548c5..964a7a065f4 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -35,6 +35,8 @@ general_settings: LANGFUSE_SECRET_KEY: "os.environ/LANGFUSE_DEV_SK_KEY" litellm_settings: + cache: true + callbacks: ["otel"] guardrails: - prompt_injection: callbacks: [lakera_prompt_injection, hide_secrets] From cb6ddaf1f9e38653dec17a45c56ee9123b5862d8 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 08:01:18 -0700 Subject: [PATCH 49/89] test - otel spans --- litellm/tests/test_async_opentelemetry.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/litellm/tests/test_async_opentelemetry.py b/litellm/tests/test_async_opentelemetry.py index 4d174c05051..fa9fc511073 100644 --- a/litellm/tests/test_async_opentelemetry.py +++ b/litellm/tests/test_async_opentelemetry.py @@ -1,21 +1,19 @@ import asyncio -import litellm - -from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig -from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter -from litellm._logging import verbose_logger import logging import time + import pytest +from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter + +import litellm +from litellm._logging import verbose_logger +from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig verbose_logger.setLevel(logging.DEBUG) -@pytest.mark.skip( - reason="new test. WIP. works locally but not on CI. Still figuring this out" -) @pytest.mark.asyncio -async def test_otel_callback(): +async def test_async_otel_callback(): exporter = InMemorySpanExporter() litellm.set_verbose = True litellm.callbacks = [OpenTelemetry(OpenTelemetryConfig(exporter=exporter))] From 02ab3cb73d93d71a7bc73ec4feb397100978e11d Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 08:47:16 -0700 Subject: [PATCH 50/89] test- otel span recording --- .circleci/config.yml | 98 ++++++++++++++++ litellm/integrations/opentelemetry.py | 6 + litellm/proxy/common_utils/debug_utils.py | 16 +++ .../example_config_yaml/otel_test_config.yaml | 11 ++ tests/otel_tests/test_otel.py | 111 ++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 litellm/proxy/example_config_yaml/otel_test_config.yaml create mode 100644 tests/otel_tests/test_otel.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 40d498d6e7f..58a3cfd6c67 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -249,6 +249,104 @@ jobs: # Store test results - store_test_results: path: test-results + proxy_log_to_otel_tests: + machine: + image: ubuntu-2204:2023.10.1 + resource_class: xlarge + working_directory: ~/project + steps: + - checkout + - run: + name: Install Docker CLI (In case it's not already installed) + command: | + sudo apt-get update + sudo apt-get install -y docker-ce docker-ce-cli containerd.io + - run: + name: Install Python 3.9 + command: | + curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh --output miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda + export PATH="$HOME/miniconda/bin:$PATH" + conda init bash + source ~/.bashrc + conda create -n myenv python=3.9 -y + conda activate myenv + python --version + - run: + name: Install Dependencies + command: | + pip install "pytest==7.3.1" + pip install "pytest-asyncio==0.21.1" + pip install aiohttp + pip install openai + python -m pip install --upgrade pip + python -m pip install -r .circleci/requirements.txt + pip install "pytest==7.3.1" + pip install "pytest-mock==3.12.0" + pip install "pytest-asyncio==0.21.1" + pip install mypy + pip install "google-generativeai==0.3.2" + pip install "google-cloud-aiplatform==1.43.0" + pip install pyarrow + pip install "boto3==1.34.34" + pip install "aioboto3==12.3.0" + pip install numpydoc + pip install prisma + pip install fastapi + pip install jsonschema + pip install "httpx==0.24.1" + pip install "gunicorn==21.2.0" + pip install "anyio==3.7.1" + pip install "aiodynamo==23.10.1" + pip install "asyncio==3.4.3" + pip install "PyGithub==1.59.1" + - run: + name: Build Docker image + command: docker build -t my-app:latest -f Dockerfile.database . + - run: + name: Run Docker container + command: | + docker run -d \ + -p 4000:4000 \ + -e DATABASE_URL=$PROXY_DATABASE_URL \ + -e REDIS_HOST=$REDIS_HOST \ + -e REDIS_PASSWORD=$REDIS_PASSWORD \ + -e REDIS_PORT=$REDIS_PORT \ + -e OPENAI_API_KEY=$OPENAI_API_KEY \ + -e LITELLM_LICENSE=$LITELLM_LICENSE \ + -e OTEL_EXPORTER="in_memory" \ + --name my-app \ + -v $(pwd)/litellm/proxy/example_config_yaml/otel_test_config.yaml.yaml:/app/config.yaml \ + my-app:latest \ + --config /app/config.yaml \ + --port 4000 \ + --detailed_debug \ + - run: + name: Install curl and dockerize + command: | + sudo apt-get update + sudo apt-get install -y curl + sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz + sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz + sudo rm dockerize-linux-amd64-v0.6.1.tar.gz + - run: + name: Start outputting logs + command: docker logs -f my-app + background: true + - run: + name: Wait for app to be ready + command: dockerize -wait http://localhost:4000 -timeout 5m + - run: + name: Run tests + command: | + pwd + ls + python -m pytest -vv tests/otel_tests/test_otel.py -x --junitxml=test-results/junit.xml --durations=5 + no_output_timeout: 120m + + # Store test results + - store_test_results: + path: test-results publish_to_pypi: docker: diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index c15161fc7b5..4ed56111629 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -52,6 +52,12 @@ class OpenTelemetryConfig: OTEL_HEADERS gets sent as headers = {"x-honeycomb-team": "B85YgLm96******"} """ + from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, + ) + + if os.getenv("OTEL_EXPORTER") == "in_memory": + return cls(exporter=InMemorySpanExporter()) return cls( exporter=os.getenv("OTEL_EXPORTER", "console"), endpoint=os.getenv("OTEL_ENDPOINT"), diff --git a/litellm/proxy/common_utils/debug_utils.py b/litellm/proxy/common_utils/debug_utils.py index dc77958a62a..6c96967aad8 100644 --- a/litellm/proxy/common_utils/debug_utils.py +++ b/litellm/proxy/common_utils/debug_utils.py @@ -25,3 +25,19 @@ if os.environ.get("LITELLM_PROFILE", "false").lower() == "true": result.append(f"{stat.traceback.format()}: {stat.size / 1024} KiB") return {"top_50_memory_usage": result} + + +@router.get("/otel-spans", include_in_schema=False) +async def get_otel_spans(): + from litellm.integrations.opentelemetry import OpenTelemetry + from litellm.proxy.proxy_server import open_telemetry_logger + + open_telemetry_logger: OpenTelemetry = open_telemetry_logger + otel_exporter = open_telemetry_logger.OTEL_EXPORTER + recorded_spans = otel_exporter.get_finished_spans() + + print("Spans: ", recorded_spans) # noqa + + # these are otel spans - get the span name + span_names = [span.name for span in recorded_spans] + return {"otel_spans": span_names} diff --git a/litellm/proxy/example_config_yaml/otel_test_config.yaml b/litellm/proxy/example_config_yaml/otel_test_config.yaml new file mode 100644 index 00000000000..2e253744332 --- /dev/null +++ b/litellm/proxy/example_config_yaml/otel_test_config.yaml @@ -0,0 +1,11 @@ +model_list: + - model_name: fake-openai-endpoint + litellm_params: + model: openai/fake + api_key: fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ + +litellm_settings: + cache: true + callbacks: ["otel"] + diff --git a/tests/otel_tests/test_otel.py b/tests/otel_tests/test_otel.py new file mode 100644 index 00000000000..a07e93e3f9e --- /dev/null +++ b/tests/otel_tests/test_otel.py @@ -0,0 +1,111 @@ +# What this tests ? +## Tests /chat/completions by generating a key and then making a chat completions request +import pytest +import asyncio +import aiohttp, openai +from openai import OpenAI, AsyncOpenAI +from typing import Optional, List, Union + + +async def generate_key( + session, + models=[ + "gpt-4", + "text-embedding-ada-002", + "dall-e-2", + "fake-openai-endpoint", + "mistral-embed", + ], +): + url = "http://0.0.0.0:4000/key/generate" + headers = {"Authorization": "Bearer sk-1234", "Content-Type": "application/json"} + data = { + "models": models, + "duration": None, + } + + async with session.post(url, headers=headers, json=data) as response: + status = response.status + response_text = await response.text() + + print(response_text) + print() + + if status != 200: + raise Exception(f"Request did not return a 200 status code: {status}") + + return await response.json() + + +async def chat_completion(session, key, model: Union[str, List] = "gpt-4"): + url = "http://0.0.0.0:4000/chat/completions" + headers = { + "Authorization": f"Bearer {key}", + "Content-Type": "application/json", + } + data = { + "model": model, + "messages": [ + {"role": "user", "content": "Hello!"}, + ], + } + + async with session.post(url, headers=headers, json=data) as response: + status = response.status + response_text = await response.text() + + print(response_text) + print() + + if status != 200: + raise Exception(f"Request did not return a 200 status code: {status}") + + return await response.json() + + +async def get_otel_spans(session, key): + url = "http://0.0.0.0:4000/otel-spans" + headers = { + "Authorization": f"Bearer {key}", + "Content-Type": "application/json", + } + + async with session.get(url, headers=headers) as response: + status = response.status + response_text = await response.text() + + print(response_text) + print() + + if status != 200: + raise Exception(f"Request did not return a 200 status code: {status}") + + return await response.json() + + +@pytest.mark.asyncio +async def test_chat_completion_check_otel_spans(): + """ + - Create key + Make chat completion call + - Create user + make chat completion call + """ + async with aiohttp.ClientSession() as session: + key_gen = await generate_key(session=session) + key = key_gen["key"] + await chat_completion(session=session, key=key, model="fake-openai-endpoint") + + otel_spans = await get_otel_spans(session=session, key=key) + print("otel_spans: ", otel_spans) + + all_otel_spans = otel_spans["otel_spans"] + + assert len(all_otel_spans) == 5 + + # 'postgres', 'redis', 'raw_gen_ai_request', 'litellm_request', 'Received Proxy Server Request' in the span + assert "postgres" in all_otel_spans + assert "redis" in all_otel_spans + assert "raw_gen_ai_request" in all_otel_spans + assert "litellm_request" in all_otel_spans + assert "Received Proxy Server Request" in all_otel_spans From 5cc725078d75f160ac4c0acb4e9de878e0a72281 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 08:53:38 -0700 Subject: [PATCH 51/89] ci/cd run otel test --- .circleci/config.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 58a3cfd6c67..190b3fe2cdd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -435,6 +435,12 @@ workflows: only: - main - /litellm_.*/ + - proxy_log_to_otel_tests: + filters: + branches: + only: + - main + - /litellm_.*/ - installing_litellm_on_python: filters: branches: @@ -445,6 +451,7 @@ workflows: requires: - local_testing - build_and_test + - proxy_log_to_otel_tests filters: branches: only: From 42ae3d494dbfde95e2da5e10195eae28f61836c0 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 08:59:33 -0700 Subject: [PATCH 52/89] fix otel_test_config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 190b3fe2cdd..c7145e93e60 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -316,7 +316,7 @@ jobs: -e LITELLM_LICENSE=$LITELLM_LICENSE \ -e OTEL_EXPORTER="in_memory" \ --name my-app \ - -v $(pwd)/litellm/proxy/example_config_yaml/otel_test_config.yaml.yaml:/app/config.yaml \ + -v $(pwd)/litellm/proxy/example_config_yaml/otel_test_config.yaml:/app/config.yaml \ my-app:latest \ --config /app/config.yaml \ --port 4000 \ From 57607dfc470b20a33cd684eea932f845fb7c69d0 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 07:58:26 -0700 Subject: [PATCH 53/89] test(test_alangfuse.py): fix test to expect correct response object --- litellm/tests/langfuse.log | 159 +++++++++++++++++--------------- litellm/tests/test_alangfuse.py | 9 +- 2 files changed, 91 insertions(+), 77 deletions(-) diff --git a/litellm/tests/langfuse.log b/litellm/tests/langfuse.log index 3d38395aba4..6a59a8ff090 100644 --- a/litellm/tests/langfuse.log +++ b/litellm/tests/langfuse.log @@ -8,46 +8,70 @@ `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. -Creating trace id='22019f81-81fc-4b65-8ab3-e141f6625ac9' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 49, 2109, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='22019f81-81fc-4b65-8ab3-e141f6625ac9' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 48, 998308) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-48-998308_chatcmpl-4fc2302c-b7e4-4703-b716-cd01a3d0b322' end_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 719) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 719) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... -item size 459 -item size 887 -Creating trace id='4e3773e6-037a-43e5-825d-b712babc0daf' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 49, 3537, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating trace id='bf11b810-c475-4a96-b0ec-a581321a44af' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 49, 3618, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='bf11b810-c475-4a96-b0ec-a581321a44af' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 48, 998866) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-48-998866_chatcmpl-71dc0d36-7dfa-48af-ba45-613c2a31a6ae' end_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 1253) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 1253) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... -item size 459 -Creating generation trace_id='4e3773e6-037a-43e5-825d-b712babc0daf' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 48, 999544) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-48-999544_chatcmpl-d61b3ab9-3b9d-4913-afa5-e84420c90ac5' end_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 1850) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 1850) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... -item size 459 -item size 887 -item size 887 -Creating trace id='4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 49, 5826, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 48, 999955) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-48-999955_chatcmpl-f1c470f6-2f80-4d4a-89a4-4a013bae4085' end_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 5114) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 5114) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... -item size 459 -item size 887 -Creating trace id='8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 49, 6797, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 372) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-49-000372_chatcmpl-f2672341-93c9-4fec-bccb-665a1df53ce5' end_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 5904) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 49, 5904) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... -item size 459 -item size 887 +Creating trace id='8a5b5991-4a10-4dfe-ae1d-52a9195fcde1' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 41, 996862, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating trace id='dba8aaeb-d435-4c82-94f9-9615ace72313' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 41, 998554, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='dba8aaeb-d435-4c82-94f9-9615ace72313' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 993400) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-41-993400_chatcmpl-296c9e6a-4142-431c-86ba-c68b550c8005' end_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 995938) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 995938) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... +item size 502 +item size 930 +Creating generation trace_id='8a5b5991-4a10-4dfe-ae1d-52a9195fcde1' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 992764) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-41-992764_chatcmpl-d449c7f4-47c5-4e46-a9f1-4cd2c1fcf257' end_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 995293) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 995293) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... +item size 502 +item size 930 +Creating trace id='d8d919d4-0c8d-47c5-b1a8-38d884379c74' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 42, 766, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='d8d919d4-0c8d-47c5-b1a8-38d884379c74' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 994499) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-41-994499_chatcmpl-5b902172-1250-46e4-bcdd-fd071b951f8c' end_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 996988) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 996988) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... +Creating trace id='fac644ff-5f4c-4b45-a55d-a5c4f93ceeec' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 42, 1164, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +item size 502 +item size 930 +Creating generation trace_id='fac644ff-5f4c-4b45-a55d-a5c4f93ceeec' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 993972) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-41-993972_chatcmpl-ecc3628b-922b-430d-85b5-068bc01b9cb9' end_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 996217) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 996217) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... +item size 502 +item size 930 +Creating trace id='979fb198-f54d-4e11-ba5c-dec4b5906322' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 42, 2797, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='979fb198-f54d-4e11-ba5c-dec4b5906322' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 41, 995013) metadata={'litellm_response_cost': None, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-41-995013_chatcmpl-41cfe580-e870-4af9-830d-53472ba99028' end_time=datetime.datetime(2024, 7, 11, 7, 57, 42, 2338) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 42, 2338) model='chatgpt-v-2' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=None) prompt_name=None prompt_version=None... +item size 502 +item size 930 `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. ~0 items in the Langfuse queue uploading batch of 10 items -uploading data: {'batch': [{'id': '8709b135-ff1a-454b-8e77-9c16c2ae590b', 'type': 'trace-create', 'body': {'id': '22019f81-81fc-4b65-8ab3-e141f6625ac9', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 2109, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 2578, tzinfo=datetime.timezone.utc)}, {'id': 'ffc73bd8-2f00-496e-8566-eaa4b67b1f2b', 'type': 'generation-create', 'body': {'traceId': '22019f81-81fc-4b65-8ab3-e141f6625ac9', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 48, 998308), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'level': , 'id': 'time-17-49-48-998308_chatcmpl-4fc2302c-b7e4-4703-b716-cd01a3d0b322', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 719), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 719), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 2939, tzinfo=datetime.timezone.utc)}, {'id': '446c9edc-59e2-4805-946b-acdc25feb203', 'type': 'trace-create', 'body': {'id': 'bf11b810-c475-4a96-b0ec-a581321a44af', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 3618, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 3813, tzinfo=datetime.timezone.utc)}, {'id': '52a24782-ba7a-4bdf-9ae5-c79a5f4a8683', 'type': 'trace-create', 'body': {'id': '4e3773e6-037a-43e5-825d-b712babc0daf', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 3537, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 4113, tzinfo=datetime.timezone.utc)}, {'id': 'cce474cc-8881-4b76-8e33-64fcfb3333a5', 'type': 'generation-create', 'body': {'traceId': 'bf11b810-c475-4a96-b0ec-a581321a44af', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 48, 998866), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'level': , 'id': 'time-17-49-48-998866_chatcmpl-71dc0d36-7dfa-48af-ba45-613c2a31a6ae', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 1253), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 1253), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 4357, tzinfo=datetime.timezone.utc)}, {'id': 'b688cd33-a978-459b-9bee-330c1ea3dc07', 'type': 'generation-create', 'body': {'traceId': '4e3773e6-037a-43e5-825d-b712babc0daf', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 48, 999544), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'level': , 'id': 'time-17-49-48-999544_chatcmpl-d61b3ab9-3b9d-4913-afa5-e84420c90ac5', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 1850), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 1850), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 4708, tzinfo=datetime.timezone.utc)}, {'id': '9142c397-6a65-43a4-b4e2-4f24df372f46', 'type': 'trace-create', 'body': {'id': '4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 5826, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 6019, tzinfo=datetime.timezone.utc)}, {'id': '58990902-d78f-4e3c-ab93-806502bc273d', 'type': 'generation-create', 'body': {'traceId': '4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 48, 999955), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'level': , 'id': 'time-17-49-48-999955_chatcmpl-f1c470f6-2f80-4d4a-89a4-4a013bae4085', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 5114), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 5114), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 6245, tzinfo=datetime.timezone.utc)}, {'id': 'f7dccb63-fb83-4523-81dd-dc218e482a0e', 'type': 'trace-create', 'body': {'id': '8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 6797, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 6918, tzinfo=datetime.timezone.utc)}, {'id': '89095577-def6-40c2-93f5-c073d3772dd8', 'type': 'generation-create', 'body': {'traceId': '8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 372), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant'}, 'level': , 'id': 'time-17-49-49-000372_chatcmpl-f2672341-93c9-4fec-bccb-665a1df53ce5', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 5904), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 49, 5904), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 49, 7234, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 10, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.32.0', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} -making request: {"batch": [{"id": "8709b135-ff1a-454b-8e77-9c16c2ae590b", "type": "trace-create", "body": {"id": "22019f81-81fc-4b65-8ab3-e141f6625ac9", "timestamp": "2024-07-07T00:49:49.002109Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:49.002578Z"}, {"id": "ffc73bd8-2f00-496e-8566-eaa4b67b1f2b", "type": "generation-create", "body": {"traceId": "22019f81-81fc-4b65-8ab3-e141f6625ac9", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:48.998308-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-48-998308_chatcmpl-4fc2302c-b7e4-4703-b716-cd01a3d0b322", "endTime": "2024-07-06T17:49:49.000719-07:00", "completionStartTime": "2024-07-06T17:49:49.000719-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-07T00:49:49.002939Z"}, {"id": "446c9edc-59e2-4805-946b-acdc25feb203", "type": "trace-create", "body": {"id": "bf11b810-c475-4a96-b0ec-a581321a44af", "timestamp": "2024-07-07T00:49:49.003618Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:49.003813Z"}, {"id": "52a24782-ba7a-4bdf-9ae5-c79a5f4a8683", "type": "trace-create", "body": {"id": "4e3773e6-037a-43e5-825d-b712babc0daf", "timestamp": "2024-07-07T00:49:49.003537Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:49.004113Z"}, {"id": "cce474cc-8881-4b76-8e33-64fcfb3333a5", "type": "generation-create", "body": {"traceId": "bf11b810-c475-4a96-b0ec-a581321a44af", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:48.998866-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-48-998866_chatcmpl-71dc0d36-7dfa-48af-ba45-613c2a31a6ae", "endTime": "2024-07-06T17:49:49.001253-07:00", "completionStartTime": "2024-07-06T17:49:49.001253-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-07T00:49:49.004357Z"}, {"id": "b688cd33-a978-459b-9bee-330c1ea3dc07", "type": "generation-create", "body": {"traceId": "4e3773e6-037a-43e5-825d-b712babc0daf", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:48.999544-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-48-999544_chatcmpl-d61b3ab9-3b9d-4913-afa5-e84420c90ac5", "endTime": "2024-07-06T17:49:49.001850-07:00", "completionStartTime": "2024-07-06T17:49:49.001850-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-07T00:49:49.004708Z"}, {"id": "9142c397-6a65-43a4-b4e2-4f24df372f46", "type": "trace-create", "body": {"id": "4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa", "timestamp": "2024-07-07T00:49:49.005826Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:49.006019Z"}, {"id": "58990902-d78f-4e3c-ab93-806502bc273d", "type": "generation-create", "body": {"traceId": "4a8fa429-f08d-4f6b-b3ca-9a404d7c61fa", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:48.999955-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-48-999955_chatcmpl-f1c470f6-2f80-4d4a-89a4-4a013bae4085", "endTime": "2024-07-06T17:49:49.005114-07:00", "completionStartTime": "2024-07-06T17:49:49.005114-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-07T00:49:49.006245Z"}, {"id": "f7dccb63-fb83-4523-81dd-dc218e482a0e", "type": "trace-create", "body": {"id": "8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0", "timestamp": "2024-07-07T00:49:49.006797Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:49.006918Z"}, {"id": "89095577-def6-40c2-93f5-c073d3772dd8", "type": "generation-create", "body": {"traceId": "8da0e9ce-8b13-4b4d-9f0d-daa3d5e0afc0", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:49.000372-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-49-000372_chatcmpl-f2672341-93c9-4fec-bccb-665a1df53ce5", "endTime": "2024-07-06T17:49:49.005904-07:00", "completionStartTime": "2024-07-06T17:49:49.005904-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-07T00:49:49.007234Z"}], "metadata": {"batch_size": 10, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.32.0", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion -received response: {"errors":[],"successes":[{"id":"8709b135-ff1a-454b-8e77-9c16c2ae590b","status":201},{"id":"ffc73bd8-2f00-496e-8566-eaa4b67b1f2b","status":201},{"id":"446c9edc-59e2-4805-946b-acdc25feb203","status":201},{"id":"52a24782-ba7a-4bdf-9ae5-c79a5f4a8683","status":201},{"id":"cce474cc-8881-4b76-8e33-64fcfb3333a5","status":201},{"id":"b688cd33-a978-459b-9bee-330c1ea3dc07","status":201},{"id":"9142c397-6a65-43a4-b4e2-4f24df372f46","status":201},{"id":"58990902-d78f-4e3c-ab93-806502bc273d","status":201},{"id":"f7dccb63-fb83-4523-81dd-dc218e482a0e","status":201},{"id":"89095577-def6-40c2-93f5-c073d3772dd8","status":201}]} +uploading data: {'batch': [{'id': 'b4eb919c-efde-48b5-9113-b089b46746c3', 'type': 'trace-create', 'body': {'id': 'dba8aaeb-d435-4c82-94f9-9615ace72313', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 998554, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 998725, tzinfo=datetime.timezone.utc)}, {'id': 'ef605d6e-effe-43eb-894b-57cdc6e28ae4', 'type': 'generation-create', 'body': {'traceId': 'dba8aaeb-d435-4c82-94f9-9615ace72313', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 993400), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-41-993400_chatcmpl-296c9e6a-4142-431c-86ba-c68b550c8005', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 995938), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 995938), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 999054, tzinfo=datetime.timezone.utc)}, {'id': '34ab3341-9b8f-45ef-ba32-31d6678c12e1', 'type': 'trace-create', 'body': {'id': '8a5b5991-4a10-4dfe-ae1d-52a9195fcde1', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 996862, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 999535, tzinfo=datetime.timezone.utc)}, {'id': '17e0038d-eb35-47f0-8501-2e9298e25ab3', 'type': 'generation-create', 'body': {'traceId': '8a5b5991-4a10-4dfe-ae1d-52a9195fcde1', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 992764), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-41-992764_chatcmpl-d449c7f4-47c5-4e46-a9f1-4cd2c1fcf257', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 995293), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 995293), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 41, 999833, tzinfo=datetime.timezone.utc)}, {'id': '282dc858-0b89-44b2-9946-1b4580a3b99d', 'type': 'trace-create', 'body': {'id': 'd8d919d4-0c8d-47c5-b1a8-38d884379c74', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 766, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 890, tzinfo=datetime.timezone.utc)}, {'id': '8de8e06e-69a5-4636-a1be-7ce1179b7607', 'type': 'generation-create', 'body': {'traceId': 'd8d919d4-0c8d-47c5-b1a8-38d884379c74', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 994499), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-41-994499_chatcmpl-5b902172-1250-46e4-bcdd-fd071b951f8c', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 996988), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 996988), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 1121, tzinfo=datetime.timezone.utc)}, {'id': '576825dd-27ff-49b2-8af0-24a6ccd5f853', 'type': 'trace-create', 'body': {'id': 'fac644ff-5f4c-4b45-a55d-a5c4f93ceeec', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 1164, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 1564, tzinfo=datetime.timezone.utc)}, {'id': '4de8b9d4-96c2-492c-b273-f1d114a710da', 'type': 'generation-create', 'body': {'traceId': 'fac644ff-5f4c-4b45-a55d-a5c4f93ceeec', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 993972), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-41-993972_chatcmpl-ecc3628b-922b-430d-85b5-068bc01b9cb9', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 996217), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 996217), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 1799, tzinfo=datetime.timezone.utc)}, {'id': 'bdbc23a6-844e-415b-b33f-ede756955c74', 'type': 'trace-create', 'body': {'id': '979fb198-f54d-4e11-ba5c-dec4b5906322', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 2797, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 2921, tzinfo=datetime.timezone.utc)}, {'id': '8b74d44d-cd6d-469f-9077-efea4e82d81b', 'type': 'generation-create', 'body': {'traceId': '979fb198-f54d-4e11-ba5c-dec4b5906322', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 41, 995013), 'metadata': {'litellm_response_cost': None, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': "It's simple to use and easy to get started", 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-41-995013_chatcmpl-41cfe580-e870-4af9-830d-53472ba99028', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 42, 2338), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 42, 2338), 'model': 'chatgpt-v-2', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': }}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 42, 3176, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 10, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.39.1', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} +making request: {"batch": [{"id": "b4eb919c-efde-48b5-9113-b089b46746c3", "type": "trace-create", "body": {"id": "dba8aaeb-d435-4c82-94f9-9615ace72313", "timestamp": "2024-07-11T14:57:41.998554Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:41.998725Z"}, {"id": "ef605d6e-effe-43eb-894b-57cdc6e28ae4", "type": "generation-create", "body": {"traceId": "dba8aaeb-d435-4c82-94f9-9615ace72313", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:41.993400-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-41-993400_chatcmpl-296c9e6a-4142-431c-86ba-c68b550c8005", "endTime": "2024-07-11T07:57:41.995938-07:00", "completionStartTime": "2024-07-11T07:57:41.995938-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-11T14:57:41.999054Z"}, {"id": "34ab3341-9b8f-45ef-ba32-31d6678c12e1", "type": "trace-create", "body": {"id": "8a5b5991-4a10-4dfe-ae1d-52a9195fcde1", "timestamp": "2024-07-11T14:57:41.996862Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:41.999535Z"}, {"id": "17e0038d-eb35-47f0-8501-2e9298e25ab3", "type": "generation-create", "body": {"traceId": "8a5b5991-4a10-4dfe-ae1d-52a9195fcde1", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:41.992764-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-41-992764_chatcmpl-d449c7f4-47c5-4e46-a9f1-4cd2c1fcf257", "endTime": "2024-07-11T07:57:41.995293-07:00", "completionStartTime": "2024-07-11T07:57:41.995293-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-11T14:57:41.999833Z"}, {"id": "282dc858-0b89-44b2-9946-1b4580a3b99d", "type": "trace-create", "body": {"id": "d8d919d4-0c8d-47c5-b1a8-38d884379c74", "timestamp": "2024-07-11T14:57:42.000766Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:42.000890Z"}, {"id": "8de8e06e-69a5-4636-a1be-7ce1179b7607", "type": "generation-create", "body": {"traceId": "d8d919d4-0c8d-47c5-b1a8-38d884379c74", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:41.994499-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-41-994499_chatcmpl-5b902172-1250-46e4-bcdd-fd071b951f8c", "endTime": "2024-07-11T07:57:41.996988-07:00", "completionStartTime": "2024-07-11T07:57:41.996988-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-11T14:57:42.001121Z"}, {"id": "576825dd-27ff-49b2-8af0-24a6ccd5f853", "type": "trace-create", "body": {"id": "fac644ff-5f4c-4b45-a55d-a5c4f93ceeec", "timestamp": "2024-07-11T14:57:42.001164Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:42.001564Z"}, {"id": "4de8b9d4-96c2-492c-b273-f1d114a710da", "type": "generation-create", "body": {"traceId": "fac644ff-5f4c-4b45-a55d-a5c4f93ceeec", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:41.993972-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-41-993972_chatcmpl-ecc3628b-922b-430d-85b5-068bc01b9cb9", "endTime": "2024-07-11T07:57:41.996217-07:00", "completionStartTime": "2024-07-11T07:57:41.996217-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-11T14:57:42.001799Z"}, {"id": "bdbc23a6-844e-415b-b33f-ede756955c74", "type": "trace-create", "body": {"id": "979fb198-f54d-4e11-ba5c-dec4b5906322", "timestamp": "2024-07-11T14:57:42.002797Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:42.002921Z"}, {"id": "8b74d44d-cd6d-469f-9077-efea4e82d81b", "type": "generation-create", "body": {"traceId": "979fb198-f54d-4e11-ba5c-dec4b5906322", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:41.995013-07:00", "metadata": {"litellm_response_cost": null, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "It's simple to use and easy to get started", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-41-995013_chatcmpl-41cfe580-e870-4af9-830d-53472ba99028", "endTime": "2024-07-11T07:57:42.002338-07:00", "completionStartTime": "2024-07-11T07:57:42.002338-07:00", "model": "chatgpt-v-2", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS"}}, "timestamp": "2024-07-11T14:57:42.003176Z"}], "metadata": {"batch_size": 10, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.39.1", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion +received response: {"errors":[],"successes":[{"id":"b4eb919c-efde-48b5-9113-b089b46746c3","status":201},{"id":"ef605d6e-effe-43eb-894b-57cdc6e28ae4","status":201},{"id":"34ab3341-9b8f-45ef-ba32-31d6678c12e1","status":201},{"id":"17e0038d-eb35-47f0-8501-2e9298e25ab3","status":201},{"id":"282dc858-0b89-44b2-9946-1b4580a3b99d","status":201},{"id":"8de8e06e-69a5-4636-a1be-7ce1179b7607","status":201},{"id":"576825dd-27ff-49b2-8af0-24a6ccd5f853","status":201},{"id":"4de8b9d4-96c2-492c-b273-f1d114a710da","status":201},{"id":"bdbc23a6-844e-415b-b33f-ede756955c74","status":201},{"id":"8b74d44d-cd6d-469f-9077-efea4e82d81b","status":201}]} successfully uploaded batch of 10 items +~0 items in the Langfuse queue consumer is running... `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. -Creating trace id='litellm-test-86452fed-4136-4b1e-8083-803b21511bcc' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 50, 228138, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='litellm-test-86452fed-4136-4b1e-8083-803b21511bcc' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 50, 227128) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-50-227128_chatcmpl-14db8e8d-39a5-4b84-a761-572f1ee2c1dd' end_time=datetime.datetime(2024, 7, 6, 17, 49, 50, 227686) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 50, 227686) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'stream': False, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... +Creating trace id='litellm-test-41e731d8-eec7-484d-a123-3f5369227667' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 43, 673224, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='litellm-test-41e731d8-eec7-484d-a123-3f5369227667' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 43, 671900) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-43-671900_chatcmpl-84840e13-5f25-494b-a771-43d6dc9f4825' end_time=datetime.datetime(2024, 7, 11, 7, 57, 43, 672638) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 43, 672638) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'stream': False, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... +item size 497 +item size 999 flushing queue -item size 454 successfully flushed about 0 items. -item size 956 ~0 items in the Langfuse queue uploading batch of 2 items -uploading data: {'batch': [{'id': '1d38c06a-12d5-4488-b3d4-5f39621c85ed', 'type': 'trace-create', 'body': {'id': 'litellm-test-86452fed-4136-4b1e-8083-803b21511bcc', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 50, 228138, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 50, 228332, tzinfo=datetime.timezone.utc)}, {'id': 'ec542e6b-eee8-471b-bf44-8d89e06ac3f5', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-86452fed-4136-4b1e-8083-803b21511bcc', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 50, 227128), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant'}, 'level': , 'id': 'time-17-49-50-227128_chatcmpl-14db8e8d-39a5-4b84-a761-572f1ee2c1dd', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 50, 227686), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 50, 227686), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'stream': False, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 50, 228609, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.32.0', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} -making request: {"batch": [{"id": "1d38c06a-12d5-4488-b3d4-5f39621c85ed", "type": "trace-create", "body": {"id": "litellm-test-86452fed-4136-4b1e-8083-803b21511bcc", "timestamp": "2024-07-07T00:49:50.228138Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:50.228332Z"}, {"id": "ec542e6b-eee8-471b-bf44-8d89e06ac3f5", "type": "generation-create", "body": {"traceId": "litellm-test-86452fed-4136-4b1e-8083-803b21511bcc", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:50.227128-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-50-227128_chatcmpl-14db8e8d-39a5-4b84-a761-572f1ee2c1dd", "endTime": "2024-07-06T17:49:50.227686-07:00", "completionStartTime": "2024-07-06T17:49:50.227686-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "stream": false, "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-07T00:49:50.228609Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.32.0", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion -received response: {"errors":[],"successes":[{"id":"1d38c06a-12d5-4488-b3d4-5f39621c85ed","status":201},{"id":"ec542e6b-eee8-471b-bf44-8d89e06ac3f5","status":201}]} +uploading data: {'batch': [{'id': 'b37f0713-1089-44ec-99d2-12bd69d6b4ed', 'type': 'trace-create', 'body': {'id': 'litellm-test-41e731d8-eec7-484d-a123-3f5369227667', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 43, 673224, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 43, 673428, tzinfo=datetime.timezone.utc)}, {'id': '9e0c04ab-30c0-47ec-8507-9877c9bff1c7', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-41e731d8-eec7-484d-a123-3f5369227667', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 43, 671900), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-43-671900_chatcmpl-84840e13-5f25-494b-a771-43d6dc9f4825', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 43, 672638), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 43, 672638), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'stream': False, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 43, 673780, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.39.1', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} +making request: {"batch": [{"id": "b37f0713-1089-44ec-99d2-12bd69d6b4ed", "type": "trace-create", "body": {"id": "litellm-test-41e731d8-eec7-484d-a123-3f5369227667", "timestamp": "2024-07-11T14:57:43.673224Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:43.673428Z"}, {"id": "9e0c04ab-30c0-47ec-8507-9877c9bff1c7", "type": "generation-create", "body": {"traceId": "litellm-test-41e731d8-eec7-484d-a123-3f5369227667", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:43.671900-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-43-671900_chatcmpl-84840e13-5f25-494b-a771-43d6dc9f4825", "endTime": "2024-07-11T07:57:43.672638-07:00", "completionStartTime": "2024-07-11T07:57:43.672638-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "stream": false, "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-11T14:57:43.673780Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.39.1", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion +~0 items in the Langfuse queue +received response: {"errors":[],"successes":[{"id":"b37f0713-1089-44ec-99d2-12bd69d6b4ed","status":201},{"id":"9e0c04ab-30c0-47ec-8507-9877c9bff1c7","status":201}]} +successfully uploaded batch of 2 items +~0 items in the Langfuse queue +~0 items in the Langfuse queue +~0 items in the Langfuse queue +~0 items in the Langfuse queue +Getting observations... None, None, None, None, litellm-test-41e731d8-eec7-484d-a123-3f5369227667, None, None, None, GENERATION +~0 items in the Langfuse queue +~0 items in the Langfuse queue +consumer is running... +`litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. +flushing queue +successfully flushed about 0 items. +~0 items in the Langfuse queue +Creating trace id='litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 46, 266724, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +item size 497 +Creating generation trace_id='litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 46, 11092) metadata={'litellm_response_cost': 4.1e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-46-011092_chatcmpl-bbd13417-0ddd-46d0-a4fc-ded148ff2ae9' end_time=datetime.datetime(2024, 7, 11, 7, 57, 46, 22279) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 46, 13610) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'stream': True, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=14, output=10, total=None, unit=, input_cost=None, output_cost=None, total_cost=4.1e-05) prompt_name=None prompt_version=None... +item size 968 +~0 items in the Langfuse queue +uploading batch of 2 items +uploading data: {'batch': [{'id': 'b05c61c3-d1c8-49fa-8bbe-7c9d1a9aba87', 'type': 'trace-create', 'body': {'id': 'litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 46, 266724, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 46, 267376, tzinfo=datetime.timezone.utc)}, {'id': 'f1e3d54a-800e-40ba-8214-50fa8ba1a40e', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 46, 11092), 'metadata': {'litellm_response_cost': 4.1e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-46-011092_chatcmpl-bbd13417-0ddd-46d0-a4fc-ded148ff2ae9', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 46, 22279), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 46, 13610), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'stream': True, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 14, 'output': 10, 'unit': , 'totalCost': 4.1e-05}}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 46, 268553, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.39.1', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} +making request: {"batch": [{"id": "b05c61c3-d1c8-49fa-8bbe-7c9d1a9aba87", "type": "trace-create", "body": {"id": "litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21", "timestamp": "2024-07-11T14:57:46.266724Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:46.267376Z"}, {"id": "f1e3d54a-800e-40ba-8214-50fa8ba1a40e", "type": "generation-create", "body": {"traceId": "litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:46.011092-07:00", "metadata": {"litellm_response_cost": 4.1e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-46-011092_chatcmpl-bbd13417-0ddd-46d0-a4fc-ded148ff2ae9", "endTime": "2024-07-11T07:57:46.022279-07:00", "completionStartTime": "2024-07-11T07:57:46.013610-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "stream": true, "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 14, "output": 10, "unit": "TOKENS", "totalCost": 4.1e-05}}, "timestamp": "2024-07-11T14:57:46.268553Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.39.1", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion +received response: {"errors":[],"successes":[{"id":"b05c61c3-d1c8-49fa-8bbe-7c9d1a9aba87","status":201},{"id":"f1e3d54a-800e-40ba-8214-50fa8ba1a40e","status":201}]} successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue @@ -55,53 +79,28 @@ successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue -Getting observations... None, None, None, None, litellm-test-86452fed-4136-4b1e-8083-803b21511bcc, None, GENERATION -~0 items in the Langfuse queue -consumer is running... -`litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. -flushing queue -successfully flushed about 0 items. ~0 items in the Langfuse queue ~0 items in the Langfuse queue -Creating trace id='litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 52, 866371, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 52, 561285) metadata={'litellm_response_cost': 4.1e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]} output={'content': 'redacted-by-litellm', 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-52-561285_chatcmpl-8079666e-d514-45b8-829f-887b4ae98ec0' end_time=datetime.datetime(2024, 7, 6, 17, 49, 52, 572272) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 52, 564390) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'stream': True, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=14, output=10, total=None, unit=, input_cost=None, output_cost=None, total_cost=4.1e-05) prompt_name=None prompt_version=None... -item size 454 -item size 925 -~0 items in the Langfuse queue -~0 items in the Langfuse queue -uploading batch of 2 items -uploading data: {'batch': [{'id': '892fa3ad-9191-4f19-b050-0badbf773160', 'type': 'trace-create', 'body': {'id': 'litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 52, 866371, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 52, 866977, tzinfo=datetime.timezone.utc)}, {'id': '6abdba0f-7dba-438d-99ac-1c1a9d481d14', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 52, 561285), 'metadata': {'litellm_response_cost': 4.1e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'redacted-by-litellm'}]}, 'output': {'content': 'redacted-by-litellm', 'role': 'assistant'}, 'level': , 'id': 'time-17-49-52-561285_chatcmpl-8079666e-d514-45b8-829f-887b4ae98ec0', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 52, 572272), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 52, 564390), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'stream': True, 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 14, 'output': 10, 'unit': , 'totalCost': 4.1e-05}}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 52, 867926, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.32.0', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} -making request: {"batch": [{"id": "892fa3ad-9191-4f19-b050-0badbf773160", "type": "trace-create", "body": {"id": "litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6", "timestamp": "2024-07-07T00:49:52.866371Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:52.866977Z"}, {"id": "6abdba0f-7dba-438d-99ac-1c1a9d481d14", "type": "generation-create", "body": {"traceId": "litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:52.561285-07:00", "metadata": {"litellm_response_cost": 4.1e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "redacted-by-litellm"}]}, "output": {"content": "redacted-by-litellm", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-52-561285_chatcmpl-8079666e-d514-45b8-829f-887b4ae98ec0", "endTime": "2024-07-06T17:49:52.572272-07:00", "completionStartTime": "2024-07-06T17:49:52.564390-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "stream": true, "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 14, "output": 10, "unit": "TOKENS", "totalCost": 4.1e-05}}, "timestamp": "2024-07-07T00:49:52.867926Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.32.0", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion -~0 items in the Langfuse queue -received response: {"errors":[],"successes":[{"id":"892fa3ad-9191-4f19-b050-0badbf773160","status":201},{"id":"6abdba0f-7dba-438d-99ac-1c1a9d481d14","status":201}]} -successfully uploaded batch of 2 items -~0 items in the Langfuse queue -~0 items in the Langfuse queue -~0 items in the Langfuse queue -~0 items in the Langfuse queue -~0 items in the Langfuse queue -~0 items in the Langfuse queue -Getting observations... None, None, None, None, litellm-test-8d866db0-0114-48a2-9f24-12a82a7b3db6, None, GENERATION +Getting observations... None, None, None, None, litellm-test-e079602a-d96c-4178-a423-f45d4e8c2d21, None, None, None, GENERATION ~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue consumer is running... `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. -flushing queue -successfully flushed about 0 items. -Creating trace id='litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 54, 946118, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input='redacted-by-litellm' output='redacted-by-litellm' session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 54, 944317) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input='redacted-by-litellm' output='redacted-by-litellm' level= status_message=None parent_observation_id=None version=None id='time-17-49-54-944317_chatcmpl-0ff349c9-b506-4d5e-a511-ca47ba2b55c2' end_time=datetime.datetime(2024, 7, 6, 17, 49, 54, 945181) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 54, 945181) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... +Creating trace id='litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 48, 311181, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input='redacted-by-litellm' output='redacted-by-litellm' session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 48, 309974) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input='redacted-by-litellm' output='redacted-by-litellm' level= status_message=None parent_observation_id=None version=None id='time-07-57-48-309974_chatcmpl-9feff8c8-bddc-45fa-b3e7-3e83768589b4' end_time=datetime.datetime(2024, 7, 11, 7, 57, 48, 310635) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 48, 310635) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... item size 375 item size 860 -~0 items in the Langfuse queue -~0 items in the Langfuse queue +flushing queue +successfully flushed about 0 items. ~0 items in the Langfuse queue uploading batch of 2 items -uploading data: {'batch': [{'id': '00f5d689-e397-4ad8-b084-998a49084b84', 'type': 'trace-create', 'body': {'id': 'litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 54, 946118, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': 'redacted-by-litellm', 'output': 'redacted-by-litellm', 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 54, 946313, tzinfo=datetime.timezone.utc)}, {'id': 'c0dae082-ef3d-4884-8e9a-aede1cf07ff1', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 54, 944317), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': 'redacted-by-litellm', 'output': 'redacted-by-litellm', 'level': , 'id': 'time-17-49-54-944317_chatcmpl-0ff349c9-b506-4d5e-a511-ca47ba2b55c2', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 54, 945181), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 54, 945181), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 54, 946682, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.32.0', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} -making request: {"batch": [{"id": "00f5d689-e397-4ad8-b084-998a49084b84", "type": "trace-create", "body": {"id": "litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4", "timestamp": "2024-07-07T00:49:54.946118Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": "redacted-by-litellm", "output": "redacted-by-litellm", "tags": []}, "timestamp": "2024-07-07T00:49:54.946313Z"}, {"id": "c0dae082-ef3d-4884-8e9a-aede1cf07ff1", "type": "generation-create", "body": {"traceId": "litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:54.944317-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": "redacted-by-litellm", "output": "redacted-by-litellm", "level": "DEFAULT", "id": "time-17-49-54-944317_chatcmpl-0ff349c9-b506-4d5e-a511-ca47ba2b55c2", "endTime": "2024-07-06T17:49:54.945181-07:00", "completionStartTime": "2024-07-06T17:49:54.945181-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-07T00:49:54.946682Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.32.0", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion +uploading data: {'batch': [{'id': '414f3e3a-35b4-4e24-a64a-e9303de94d0d', 'type': 'trace-create', 'body': {'id': 'litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 48, 311181, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': 'redacted-by-litellm', 'output': 'redacted-by-litellm', 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 48, 311330, tzinfo=datetime.timezone.utc)}, {'id': '890d05df-f7e0-4c1a-ad49-6b1704c36136', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 48, 309974), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': 'redacted-by-litellm', 'output': 'redacted-by-litellm', 'level': , 'id': 'time-07-57-48-309974_chatcmpl-9feff8c8-bddc-45fa-b3e7-3e83768589b4', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 48, 310635), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 48, 310635), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 48, 311640, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.39.1', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} +~0 items in the Langfuse queue +making request: {"batch": [{"id": "414f3e3a-35b4-4e24-a64a-e9303de94d0d", "type": "trace-create", "body": {"id": "litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4", "timestamp": "2024-07-11T14:57:48.311181Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": "redacted-by-litellm", "output": "redacted-by-litellm", "tags": []}, "timestamp": "2024-07-11T14:57:48.311330Z"}, {"id": "890d05df-f7e0-4c1a-ad49-6b1704c36136", "type": "generation-create", "body": {"traceId": "litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:48.309974-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": "redacted-by-litellm", "output": "redacted-by-litellm", "level": "DEFAULT", "id": "time-07-57-48-309974_chatcmpl-9feff8c8-bddc-45fa-b3e7-3e83768589b4", "endTime": "2024-07-11T07:57:48.310635-07:00", "completionStartTime": "2024-07-11T07:57:48.310635-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-11T14:57:48.311640Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.39.1", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion ~0 items in the Langfuse queue ~0 items in the Langfuse queue -received response: {"errors":[],"successes":[{"id":"00f5d689-e397-4ad8-b084-998a49084b84","status":201},{"id":"c0dae082-ef3d-4884-8e9a-aede1cf07ff1","status":201}]} +received response: {"errors":[],"successes":[{"id":"414f3e3a-35b4-4e24-a64a-e9303de94d0d","status":201},{"id":"890d05df-f7e0-4c1a-ad49-6b1704c36136","status":201}]} successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue @@ -112,27 +111,27 @@ successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue -Getting trace litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4 +~0 items in the Langfuse queue +Getting trace litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4 ~0 items in the Langfuse queue ~0 items in the Langfuse queue +Getting observations... None, None, None, None, litellm-test-624f61d8-2604-406a-bd85-f7eb9f9878e4, None, None, None, GENERATION ~0 items in the Langfuse queue -Getting observations... None, None, None, None, litellm-test-dbb12cf8-f570-4742-91b9-16c6f23a7be4, None, GENERATION `litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs. -Creating trace id='litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb' timestamp=datetime.datetime(2024, 7, 7, 0, 49, 57, 252490, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': 'This is a test response', 'role': 'assistant'} session_id=None release=None version=None metadata=None tags=[] public=None -Creating generation trace_id='litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 6, 17, 49, 57, 251265) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': 'This is a test response', 'role': 'assistant'} level= status_message=None parent_observation_id=None version=None id='time-17-49-57-251265_chatcmpl-963c5062-9c99-4bd7-9bd2-7ff039f129e5' end_time=datetime.datetime(2024, 7, 6, 17, 49, 57, 251932) completion_start_time=datetime.datetime(2024, 7, 6, 17, 49, 57, 251932) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... -item size 453 +Creating trace id='litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be' timestamp=datetime.datetime(2024, 7, 11, 14, 57, 50, 588244, tzinfo=datetime.timezone.utc) name='litellm-acompletion' user_id='langfuse_latency_test_user' input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': 'This is a test response', 'role': 'assistant', 'tool_calls': None, 'function_call': None} session_id=None release=None version=None metadata=None tags=[] public=None +Creating generation trace_id='litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be' name='litellm-acompletion' start_time=datetime.datetime(2024, 7, 11, 7, 57, 50, 585058) metadata={'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False} input={'messages': [{'role': 'user', 'content': 'This is a test'}]} output={'content': 'This is a test response', 'role': 'assistant', 'tool_calls': None, 'function_call': None} level= status_message=None parent_observation_id=None version=None id='time-07-57-50-585058_chatcmpl-05db6b0b-4847-4b87-a8ef-7d90bef7f81c' end_time=datetime.datetime(2024, 7, 11, 7, 57, 50, 587160) completion_start_time=datetime.datetime(2024, 7, 11, 7, 57, 50, 587160) model='gpt-3.5-turbo' model_parameters={'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'} usage=Usage(input=10, output=20, total=None, unit=, input_cost=None, output_cost=None, total_cost=5.4999999999999995e-05) prompt_name=None prompt_version=None... +item size 496 +item size 981 flushing queue -item size 938 successfully flushed about 0 items. ~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue -~0 items in the Langfuse queue uploading batch of 2 items -uploading data: {'batch': [{'id': '6b7f45eb-d322-4614-9611-10b6325982da', 'type': 'trace-create', 'body': {'id': 'litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb', 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 57, 252490, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': 'This is a test response', 'role': 'assistant'}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 57, 252967, tzinfo=datetime.timezone.utc)}, {'id': '7db8d145-bb3f-4e64-958b-0e73ea777d6d', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 6, 17, 49, 57, 251265), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': 'This is a test response', 'role': 'assistant'}, 'level': , 'id': 'time-17-49-57-251265_chatcmpl-963c5062-9c99-4bd7-9bd2-7ff039f129e5', 'endTime': datetime.datetime(2024, 7, 6, 17, 49, 57, 251932), 'completionStartTime': datetime.datetime(2024, 7, 6, 17, 49, 57, 251932), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 7, 0, 49, 57, 253374, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.32.0', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} -making request: {"batch": [{"id": "6b7f45eb-d322-4614-9611-10b6325982da", "type": "trace-create", "body": {"id": "litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb", "timestamp": "2024-07-07T00:49:57.252490Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "This is a test response", "role": "assistant"}, "tags": []}, "timestamp": "2024-07-07T00:49:57.252967Z"}, {"id": "7db8d145-bb3f-4e64-958b-0e73ea777d6d", "type": "generation-create", "body": {"traceId": "litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb", "name": "litellm-acompletion", "startTime": "2024-07-06T17:49:57.251265-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "This is a test response", "role": "assistant"}, "level": "DEFAULT", "id": "time-17-49-57-251265_chatcmpl-963c5062-9c99-4bd7-9bd2-7ff039f129e5", "endTime": "2024-07-06T17:49:57.251932-07:00", "completionStartTime": "2024-07-06T17:49:57.251932-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-07T00:49:57.253374Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.32.0", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion +uploading data: {'batch': [{'id': '27ef4895-21f0-4485-b9e6-083e1d64e5fa', 'type': 'trace-create', 'body': {'id': 'litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be', 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 50, 588244, tzinfo=datetime.timezone.utc), 'name': 'litellm-acompletion', 'userId': 'langfuse_latency_test_user', 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': 'This is a test response', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'tags': []}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 50, 589416, tzinfo=datetime.timezone.utc)}, {'id': '8f77e83d-d4b4-4890-b37a-bf04611f2ed6', 'type': 'generation-create', 'body': {'traceId': 'litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be', 'name': 'litellm-acompletion', 'startTime': datetime.datetime(2024, 7, 11, 7, 57, 50, 585058), 'metadata': {'litellm_response_cost': 5.4999999999999995e-05, 'cache_hit': False}, 'input': {'messages': [{'role': 'user', 'content': 'This is a test'}]}, 'output': {'content': 'This is a test response', 'role': 'assistant', 'tool_calls': None, 'function_call': None}, 'level': , 'id': 'time-07-57-50-585058_chatcmpl-05db6b0b-4847-4b87-a8ef-7d90bef7f81c', 'endTime': datetime.datetime(2024, 7, 11, 7, 57, 50, 587160), 'completionStartTime': datetime.datetime(2024, 7, 11, 7, 57, 50, 587160), 'model': 'gpt-3.5-turbo', 'modelParameters': {'temperature': '0.7', 'max_tokens': 5, 'user': 'langfuse_latency_test_user', 'extra_body': '{}'}, 'usage': {'input': 10, 'output': 20, 'unit': , 'totalCost': 5.4999999999999995e-05}}, 'timestamp': datetime.datetime(2024, 7, 11, 14, 57, 50, 590731, tzinfo=datetime.timezone.utc)}], 'metadata': {'batch_size': 2, 'sdk_integration': 'default', 'sdk_name': 'python', 'sdk_version': '2.39.1', 'public_key': 'pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003'}} +making request: {"batch": [{"id": "27ef4895-21f0-4485-b9e6-083e1d64e5fa", "type": "trace-create", "body": {"id": "litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be", "timestamp": "2024-07-11T14:57:50.588244Z", "name": "litellm-acompletion", "userId": "langfuse_latency_test_user", "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "This is a test response", "role": "assistant", "tool_calls": null, "function_call": null}, "tags": []}, "timestamp": "2024-07-11T14:57:50.589416Z"}, {"id": "8f77e83d-d4b4-4890-b37a-bf04611f2ed6", "type": "generation-create", "body": {"traceId": "litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be", "name": "litellm-acompletion", "startTime": "2024-07-11T07:57:50.585058-07:00", "metadata": {"litellm_response_cost": 5.4999999999999995e-05, "cache_hit": false}, "input": {"messages": [{"role": "user", "content": "This is a test"}]}, "output": {"content": "This is a test response", "role": "assistant", "tool_calls": null, "function_call": null}, "level": "DEFAULT", "id": "time-07-57-50-585058_chatcmpl-05db6b0b-4847-4b87-a8ef-7d90bef7f81c", "endTime": "2024-07-11T07:57:50.587160-07:00", "completionStartTime": "2024-07-11T07:57:50.587160-07:00", "model": "gpt-3.5-turbo", "modelParameters": {"temperature": "0.7", "max_tokens": 5, "user": "langfuse_latency_test_user", "extra_body": "{}"}, "usage": {"input": 10, "output": 20, "unit": "TOKENS", "totalCost": 5.4999999999999995e-05}}, "timestamp": "2024-07-11T14:57:50.590731Z"}], "metadata": {"batch_size": 2, "sdk_integration": "default", "sdk_name": "python", "sdk_version": "2.39.1", "public_key": "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"}} to https://us.cloud.langfuse.com/api/public/ingestion ~0 items in the Langfuse queue -received response: {"errors":[],"successes":[{"id":"6b7f45eb-d322-4614-9611-10b6325982da","status":201},{"id":"7db8d145-bb3f-4e64-958b-0e73ea777d6d","status":201}]} +received response: {"errors":[],"successes":[{"id":"27ef4895-21f0-4485-b9e6-083e1d64e5fa","status":201},{"id":"8f77e83d-d4b4-4890-b37a-bf04611f2ed6","status":201}]} successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue @@ -144,10 +143,15 @@ successfully uploaded batch of 2 items ~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue -Getting trace litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb +~0 items in the Langfuse queue +Getting trace litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be +~0 items in the Langfuse queue +Getting observations... None, None, None, None, litellm-test-0020e225-3dc4-43d5-b75f-468a042e51be, None, None, None, GENERATION +~0 items in the Langfuse queue +~0 items in the Langfuse queue +~0 items in the Langfuse queue ~0 items in the Langfuse queue ~0 items in the Langfuse queue -Getting observations... None, None, None, None, litellm-test-f2b16f5b-81b8-4716-b812-3ff8d4d7b6cb, None, GENERATION ~0 items in the Langfuse queue joining 1 consumer threads ~0 items in the Langfuse queue @@ -157,9 +161,12 @@ joining 1 consumer threads consumer thread 0 joined joining 1 consumer threads ~0 items in the Langfuse queue +~0 items in the Langfuse queue +~0 items in the Langfuse queue consumer thread 0 joined joining 1 consumer threads ~0 items in the Langfuse queue +~0 items in the Langfuse queue consumer thread 0 joined joining 1 consumer threads ~0 items in the Langfuse queue diff --git a/litellm/tests/test_alangfuse.py b/litellm/tests/test_alangfuse.py index a70e35f259a..8f91ffa67a7 100644 --- a/litellm/tests/test_alangfuse.py +++ b/litellm/tests/test_alangfuse.py @@ -237,6 +237,8 @@ async def test_langfuse_logging_without_request_response(stream, langfuse_client assert _trace_data[0].output == { "role": "assistant", "content": "redacted-by-litellm", + "function_call": None, + "tool_calls": None, } except Exception as e: @@ -273,7 +275,12 @@ async def test_langfuse_masked_input_output(langfuse_client): expected_output = ( "redacted-by-litellm" if mask_value - else {"content": "This is a test response", "role": "assistant"} + else { + "content": "This is a test response", + "role": "assistant", + "function_call": None, + "tool_calls": None, + } ) langfuse_client.flush() await asyncio.sleep(2) From e2c3b2b694e6133c8af48d842368c3db86a15696 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 09:05:08 -0700 Subject: [PATCH 54/89] fix add master key in requests --- litellm/proxy/proxy_config.yaml | 42 ++------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index 964a7a065f4..5fd4e5b62c2 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -4,49 +4,11 @@ model_list: model: openai/fake api_key: fake-key api_base: https://exampleopenaiendpoint-production.up.railway.app/ - - model_name: llama3 - litellm_params: - model: groq/llama3-8b-8192 - - model_name: gpt-3.5-turbo - litellm_params: - model: gpt-3.5-turbo - - model_name: "*" - litellm_params: - model: openai/* - api_key: os.environ/OPENAI_API_KEY - - model_name: mistral-embed - litellm_params: - model: mistral/mistral-embed -general_settings: - pass_through_endpoints: - - path: "/v1/rerank" - target: "https://api.cohere.com/v1/rerank" - auth: true # 👈 Key change to use LiteLLM Auth / Keys - headers: - Authorization: "bearer os.environ/COHERE_API_KEY" - content-type: application/json - accept: application/json - - path: "/api/public/ingestion" - target: "https://us.cloud.langfuse.com/api/public/ingestion" - auth: true - headers: - LANGFUSE_PUBLIC_KEY: "os.environ/LANGFUSE_DEV_PUBLIC_KEY" - LANGFUSE_SECRET_KEY: "os.environ/LANGFUSE_DEV_SK_KEY" +general_settings: + master_key: sk-1234 litellm_settings: cache: true callbacks: ["otel"] - guardrails: - - prompt_injection: - callbacks: [lakera_prompt_injection, hide_secrets] - default_on: true - - hide_secrets: - callbacks: [hide_secrets] - default_on: true - -assistant_settings: - custom_llm_provider: openai - litellm_params: - api_key: os.environ/OPENAI_API_KEY From b5a00722bc487489a7d8c79384307d11e2e37017 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 09:20:55 -0700 Subject: [PATCH 55/89] docs(assistants.md): add openai-compatible assistants api example to docs --- docs/my-website/docs/assistants.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/my-website/docs/assistants.md b/docs/my-website/docs/assistants.md index cfbc4c11a4b..fb30a132f77 100644 --- a/docs/my-website/docs/assistants.md +++ b/docs/my-website/docs/assistants.md @@ -26,6 +26,7 @@ Call an existing Assistant. - Run the Assistant on the Thread to generate a response by calling the model and the tools. +### SDK + PROXY @@ -281,3 +282,31 @@ curl -X POST 'http://0.0.0.0:4000/threads/{thread_id}/runs' \ ## [👉 Proxy API Reference](https://litellm-api.up.railway.app/#/assistants) + +## OpenAI-Compatible APIs + +To call openai-compatible Assistants API's (eg. Astra Assistants API), just add `openai/` to the model name: + + +**config** +```yaml +assistant_settings: + custom_llm_provider: openai + litellm_params: + api_key: os.environ/ASTRA_API_KEY + api_base: os.environ/ASTRA_API_BASE +``` + +**curl** + +```bash +curl -X POST "http://localhost:4000/v1/assistants" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sk-1234" \ + -d '{ + "instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.", + "name": "Math Tutor", + "tools": [{"type": "code_interpreter"}], + "model": "openai/" + }' +``` \ No newline at end of file From 8fa2cf15eee0a8b8c062960edae2aa3716b29091 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 09:34:46 -0700 Subject: [PATCH 56/89] fix(watsonx.py): fix watson process response Fixes https://github.com/BerriAI/litellm/issues/4654 --- litellm/llms/watsonx.py | 38 +++++++++++++++++++++++--------------- litellm/types/utils.py | 4 ---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index 5649b714a0e..a3d31555748 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -25,7 +25,13 @@ import requests # type: ignore import litellm from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler -from litellm.utils import ModelResponse, Usage, get_secret +from litellm.utils import ( + EmbeddingResponse, + ModelResponse, + Usage, + get_secret, + map_finish_reason, +) from .base import BaseLLM from .prompt_templates import factory as ptf @@ -414,14 +420,16 @@ class IBMWatsonXAI(BaseLLM): generated_text = json_resp["results"][0]["generated_text"] prompt_tokens = json_resp["results"][0]["input_token_count"] completion_tokens = json_resp["results"][0]["generated_token_count"] - model_response["choices"][0]["message"]["content"] = generated_text - model_response["finish_reason"] = json_resp["results"][0]["stop_reason"] + model_response.choices[0].message.content = generated_text # type: ignore + model_response.choices[0].finish_reason = map_finish_reason( + json_resp["results"][0]["stop_reason"] + ) if json_resp.get("created_at"): - model_response["created"] = datetime.fromisoformat( - json_resp["created_at"] - ).timestamp() + model_response.created = int( + datetime.fromisoformat(json_resp["created_at"]).timestamp() + ) else: - model_response["created"] = int(time.time()) + model_response.created = int(time.time()) usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -463,7 +471,7 @@ class IBMWatsonXAI(BaseLLM): prompt = convert_messages_to_prompt( model, messages, provider, custom_prompt_dict ) - model_response["model"] = model + model_response.model = model def process_stream_response( stream_resp: Union[Iterator[str], AsyncIterator], @@ -551,10 +559,10 @@ class IBMWatsonXAI(BaseLLM): raise WatsonXAIError(status_code=500, message=str(e)) def _process_embedding_response( - self, json_resp: dict, model_response: Union[ModelResponse, None] = None - ) -> ModelResponse: + self, json_resp: dict, model_response: Optional[EmbeddingResponse] = None + ) -> EmbeddingResponse: if model_response is None: - model_response = ModelResponse(model=json_resp.get("model_id", None)) + model_response = EmbeddingResponse(model=json_resp.get("model_id", None)) results = json_resp.get("results", []) embedding_response = [] for idx, result in enumerate(results): @@ -565,8 +573,8 @@ class IBMWatsonXAI(BaseLLM): "embedding": result["embedding"], } ) - model_response["object"] = "list" - model_response["data"] = embedding_response + model_response.object = "list" + model_response.data = embedding_response input_tokens = json_resp.get("input_token_count", 0) setattr( model_response, @@ -635,12 +643,12 @@ class IBMWatsonXAI(BaseLLM): } request_manager = RequestManager(logging_obj) - def handle_embedding(request_params: dict) -> ModelResponse: + def handle_embedding(request_params: dict) -> EmbeddingResponse: with request_manager.request(request_params, input=input) as resp: json_resp = resp.json() return self._process_embedding_response(json_resp, model_response) - async def handle_aembedding(request_params: dict) -> ModelResponse: + async def handle_aembedding(request_params: dict) -> EmbeddingResponse: async with request_manager.async_request( request_params, input=input ) as resp: diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 4ae88a7450a..2e0ae425c71 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -624,10 +624,6 @@ class ModelResponse(OpenAIObject): # Allow dictionary-style access to attributes return getattr(self, key) - def __setitem__(self, key, value): - # Allow dictionary-style assignment of attributes - setattr(self, key, value) - def json(self, **kwargs): try: return self.model_dump() # noqa From 070ab9f46977ae0fff1059254236c47cf4947a55 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 09:47:50 -0700 Subject: [PATCH 57/89] docs(model_management.md): update docs to clarify calling `/model/info` --- docs/my-website/docs/proxy/model_management.md | 4 ++-- litellm/proxy/proxy_server.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/model_management.md b/docs/my-website/docs/proxy/model_management.md index 61d7e088239..02ce4ba23b6 100644 --- a/docs/my-website/docs/proxy/model_management.md +++ b/docs/my-website/docs/proxy/model_management.md @@ -15,9 +15,9 @@ model_list: metadata: "here's additional metadata on the model" # returned via GET /model/info ``` -## Get Model Information +## Get Model Information - `/model/info` -Retrieve detailed information about each model listed in the `/models` endpoint, including descriptions from the `config.yaml` file, and additional model info (e.g. max tokens, cost per input token, etc.) pulled the model_info you set and the litellm model cost map. Sensitive details like API keys are excluded for security purposes. +Retrieve detailed information about each model listed in the `/model/info` endpoint, including descriptions from the `config.yaml` file, and additional model info (e.g. max tokens, cost per input token, etc.) pulled the model_info you set and the litellm model cost map. Sensitive details like API keys are excluded for security purposes. Date: Thu, 11 Jul 2024 09:54:23 -0700 Subject: [PATCH 58/89] test otel --- litellm/proxy/common_utils/debug_utils.py | 21 ++++++++++++++++++++- tests/otel_tests/test_otel.py | 17 +++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/common_utils/debug_utils.py b/litellm/proxy/common_utils/debug_utils.py index 6c96967aad8..8306ad0fb5e 100644 --- a/litellm/proxy/common_utils/debug_utils.py +++ b/litellm/proxy/common_utils/debug_utils.py @@ -38,6 +38,25 @@ async def get_otel_spans(): print("Spans: ", recorded_spans) # noqa + most_recent_parent = None + most_recent_start_time = 1000000 + spans_grouped_by_parent = {} + for span in recorded_spans: + if span.parent is not None: + parent_trace_id = span.parent.trace_id + if parent_trace_id not in spans_grouped_by_parent: + spans_grouped_by_parent[parent_trace_id] = [] + spans_grouped_by_parent[parent_trace_id].append(span.name) + + # check time of span + if span.start_time > most_recent_start_time: + most_recent_parent = parent_trace_id + most_recent_start_time = span.start_time + # these are otel spans - get the span name span_names = [span.name for span in recorded_spans] - return {"otel_spans": span_names} + return { + "otel_spans": span_names, + "spans_grouped_by_parent": spans_grouped_by_parent, + "most_recent_parent": most_recent_parent, + } diff --git a/tests/otel_tests/test_otel.py b/tests/otel_tests/test_otel.py index a07e93e3f9e..e372ddb95f0 100644 --- a/tests/otel_tests/test_otel.py +++ b/tests/otel_tests/test_otel.py @@ -100,12 +100,17 @@ async def test_chat_completion_check_otel_spans(): print("otel_spans: ", otel_spans) all_otel_spans = otel_spans["otel_spans"] + most_recent_parent = str(otel_spans["most_recent_parent"]) + print("Most recent OTEL parent: ", most_recent_parent) + print("\n spans grouped by parent: ", otel_spans["spans_grouped_by_parent"]) + parent_trace_spans = otel_spans["spans_grouped_by_parent"][most_recent_parent] - assert len(all_otel_spans) == 5 + print("Parent trace spans: ", parent_trace_spans) + + assert len(parent_trace_spans) == 4 # 'postgres', 'redis', 'raw_gen_ai_request', 'litellm_request', 'Received Proxy Server Request' in the span - assert "postgres" in all_otel_spans - assert "redis" in all_otel_spans - assert "raw_gen_ai_request" in all_otel_spans - assert "litellm_request" in all_otel_spans - assert "Received Proxy Server Request" in all_otel_spans + assert "postgres" in parent_trace_spans + assert "redis" in parent_trace_spans + assert "raw_gen_ai_request" in parent_trace_spans + assert "litellm_request" in parent_trace_spans From 775ce119b60f3224f6cbe4a8265fa622f6637cd0 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:01:36 -0700 Subject: [PATCH 59/89] test otel --- .circleci/config.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c7145e93e60..66279260814 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -305,13 +305,15 @@ jobs: command: docker build -t my-app:latest -f Dockerfile.database . - run: name: Run Docker container + # intentionally give bad redis credentials here + # the OTEL test - should get this as a trace command: | docker run -d \ -p 4000:4000 \ -e DATABASE_URL=$PROXY_DATABASE_URL \ - -e REDIS_HOST=$REDIS_HOST \ - -e REDIS_PASSWORD=$REDIS_PASSWORD \ - -e REDIS_PORT=$REDIS_PORT \ + -e REDIS_HOST=bad-credentials \ + -e REDIS_PASSWORD=bad-credentials \ + -e REDIS_PORT=bad-credentials \ -e OPENAI_API_KEY=$OPENAI_API_KEY \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ -e OTEL_EXPORTER="in_memory" \ From ab400d24f570db33be59290c6ddb54f00d203be9 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:02:15 -0700 Subject: [PATCH 60/89] reduce deps in ci/cd test --- .circleci/config.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 66279260814..ea311ec77ab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -285,19 +285,13 @@ jobs: pip install "pytest-mock==3.12.0" pip install "pytest-asyncio==0.21.1" pip install mypy - pip install "google-generativeai==0.3.2" - pip install "google-cloud-aiplatform==1.43.0" pip install pyarrow - pip install "boto3==1.34.34" - pip install "aioboto3==12.3.0" pip install numpydoc pip install prisma pip install fastapi pip install jsonschema pip install "httpx==0.24.1" - pip install "gunicorn==21.2.0" pip install "anyio==3.7.1" - pip install "aiodynamo==23.10.1" pip install "asyncio==3.4.3" pip install "PyGithub==1.59.1" - run: From bda9eb7faaa6bf19f92f730ddb76319fb10f33a2 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:04:05 -0700 Subject: [PATCH 61/89] fix build and test test --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ea311ec77ab..a2c1c08d7c7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -243,7 +243,7 @@ jobs: command: | pwd ls - python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 + python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not test_otel.py" no_output_timeout: 120m # Store test results From 77dcae697996e90d2094b35808d0a92d58643983 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:07:39 -0700 Subject: [PATCH 62/89] fix test otel tracing --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a2c1c08d7c7..b540fd44ee6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -307,7 +307,7 @@ jobs: -e DATABASE_URL=$PROXY_DATABASE_URL \ -e REDIS_HOST=bad-credentials \ -e REDIS_PASSWORD=bad-credentials \ - -e REDIS_PORT=bad-credentials \ + -e REDIS_PORT=16337 \ -e OPENAI_API_KEY=$OPENAI_API_KEY \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ -e OTEL_EXPORTER="in_memory" \ From 73d19451bdcc2fa9c685f7f439e4cc70bc2869ba Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:16:05 -0700 Subject: [PATCH 63/89] fix test otel --- .circleci/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b540fd44ee6..06500e6b5a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -305,9 +305,10 @@ jobs: docker run -d \ -p 4000:4000 \ -e DATABASE_URL=$PROXY_DATABASE_URL \ - -e REDIS_HOST=bad-credentials \ - -e REDIS_PASSWORD=bad-credentials \ - -e REDIS_PORT=16337 \ + -e REDIS_HOST=$REDIS_HOST \ + -e REDIS_PASSWORD=$REDIS_PASSWORD \ + -e REDIS_PORT=$REDIS_PORT \ + -e LITELLM_MASTER_KEY="sk-1234" \ -e OPENAI_API_KEY=$OPENAI_API_KEY \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ -e OTEL_EXPORTER="in_memory" \ From b124d9414bd4ba7e15fd5d4bb4fd072c9b07f698 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:22:12 -0700 Subject: [PATCH 64/89] fix build and test test --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 06500e6b5a0..9d9f591757d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -243,7 +243,7 @@ jobs: command: | pwd ls - python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not test_otel.py" + python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not tests/otel_tests/test_otel.py" no_output_timeout: 120m # Store test results From 07ae8c6059c82892498ffc6d2674abfd720e6b09 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:25:34 -0700 Subject: [PATCH 65/89] fix not clause on ci/cd tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9d9f591757d..42d3133add5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -243,7 +243,7 @@ jobs: command: | pwd ls - python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not tests/otel_tests/test_otel.py" + python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not (otel_tests)" no_output_timeout: 120m # Store test results From 8d7db56deb94396cc13c4a3ce9514a331e31cf3e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:30:40 -0700 Subject: [PATCH 66/89] fix Local only test. WIP --- litellm/tests/test_async_opentelemetry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_async_opentelemetry.py b/litellm/tests/test_async_opentelemetry.py index fa9fc511073..aee434f2a4a 100644 --- a/litellm/tests/test_async_opentelemetry.py +++ b/litellm/tests/test_async_opentelemetry.py @@ -13,6 +13,7 @@ verbose_logger.setLevel(logging.DEBUG) @pytest.mark.asyncio +@pytest.mark.skip(reason="Local only test. WIP.") async def test_async_otel_callback(): exporter = InMemorySpanExporter() litellm.set_verbose = True From 6e86e4291ab748e030d1c050b34283115ca2a332 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 10:34:28 -0700 Subject: [PATCH 67/89] fix pytest --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 42d3133add5..4fad4111d3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -243,7 +243,7 @@ jobs: command: | pwd ls - python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 "not (otel_tests)" + python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 --ignore=tests/otel_tests no_output_timeout: 120m # Store test results From 2163434ff3bf2e482d6dca31d0673c191976a935 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 11:52:18 -0700 Subject: [PATCH 68/89] fix(llm_cost_calc/google.py): fix google embedding cost calculation Fixes https://github.com/BerriAI/litellm/issues/4630 --- litellm/cost_calculator.py | 30 +++++--- litellm/integrations/slack_alerting.py | 6 +- litellm/litellm_core_utils/litellm_logging.py | 2 +- .../llm_cost_calc/google.py | 28 ++++++- litellm/proxy/_new_secret_config.yaml | 12 ++- litellm/tests/test_completion_cost.py | 73 +++++++++++++++++++ 6 files changed, 133 insertions(+), 18 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 0bc65a7f1d5..13a9e4bdc6c 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -15,10 +15,12 @@ from litellm.litellm_core_utils.llm_cost_calc.google import ( from litellm.litellm_core_utils.llm_cost_calc.google import ( cost_per_token as google_cost_per_token, ) +from litellm.litellm_core_utils.llm_cost_calc.google import ( + cost_router as google_cost_router, +) from litellm.litellm_core_utils.llm_cost_calc.utils import _generic_cost_per_character from litellm.types.llms.openai import HttpxBinaryResponseContent from litellm.types.router import SPECIAL_MODEL_INFO_PARAMS - from litellm.utils import ( CallTypes, CostPerToken, @@ -160,22 +162,32 @@ def cost_per_token( # see this https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models print_verbose(f"Looking up model={model} in model_cost_map") - if custom_llm_provider == "vertex_ai" and "claude" in model: - return google_cost_per_token( - model=model_without_prefix, - custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - ) if custom_llm_provider == "vertex_ai": - return google_cost_per_character( + cost_router = google_cost_router( model=model_without_prefix, custom_llm_provider=custom_llm_provider, prompt_characters=prompt_characters, completion_characters=completion_characters, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, + call_type=call_type, ) + if cost_router == "cost_per_character": + return google_cost_per_character( + model=model_without_prefix, + custom_llm_provider=custom_llm_provider, + prompt_characters=prompt_characters, + completion_characters=completion_characters, + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + ) + elif cost_router == "cost_per_token": + return google_cost_per_token( + model=model_without_prefix, + custom_llm_provider=custom_llm_provider, + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + ) elif custom_llm_provider == "gemini": return google_cost_per_token( model=model_without_prefix, diff --git a/litellm/integrations/slack_alerting.py b/litellm/integrations/slack_alerting.py index 437e8ce135f..b7b62b61f62 100644 --- a/litellm/integrations/slack_alerting.py +++ b/litellm/integrations/slack_alerting.py @@ -1530,9 +1530,9 @@ Model Info: """Log deployment latency""" try: if "daily_reports" in self.alert_types: - model_id = ( - kwargs.get("litellm_params", {}).get("model_info", {}).get("id", "") - ) + litellm_params = kwargs.get("litellm_params", {}) or {} + model_info = litellm_params.get("model_info", {}) or {} + model_id = model_info.get("id", "") or "" response_s: timedelta = end_time - start_time final_value = response_s diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 0271c571477..0edc90325dc 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -1275,7 +1275,7 @@ class Logging: f"Model={self.model}; cost={self.model_call_details['response_cost']}" ) except litellm.NotFoundError as e: - verbose_logger.error( + verbose_logger.warning( f"Model={self.model} not found in completion cost map. Setting 'response_cost' to None" ) self.model_call_details["response_cost"] = None diff --git a/litellm/litellm_core_utils/llm_cost_calc/google.py b/litellm/litellm_core_utils/llm_cost_calc/google.py index 2c958cf88a9..76da0da51e4 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/google.py +++ b/litellm/litellm_core_utils/llm_cost_calc/google.py @@ -1,7 +1,7 @@ # What is this? ## Cost calculation for Google AI Studio / Vertex AI models import traceback -from typing import List, Literal, Optional, Tuple +from typing import List, Literal, Optional, Tuple, Union import litellm from litellm import verbose_logger @@ -29,6 +29,32 @@ def _is_above_128k(tokens: float) -> bool: return False +def cost_router( + model: str, + custom_llm_provider: str, + prompt_tokens: float, + completion_tokens: float, + prompt_characters: float, + completion_characters: float, + call_type: Union[Literal["embedding", "aembedding"], str], +) -> Literal["cost_per_character", "cost_per_token"]: + """ + Route the cost calc to the right place, based on model/call_type/etc. + + Returns + - str, the specific google cost calc function it should route to. + """ + if custom_llm_provider == "vertex_ai" and "claude" in model: + return "cost_per_token" + elif custom_llm_provider == "gemini": + return "cost_per_token" + elif custom_llm_provider == "vertex_ai" and ( + call_type == "embedding" or call_type == "aembedding" + ): + return "cost_per_token" + return "cost_per_character" + + def cost_per_character( model: str, custom_llm_provider: str, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 0f1f981d7ad..a8c9e882333 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -1,10 +1,14 @@ model_list: - - model_name: "*" + - model_name: azure-ai-mistral litellm_params: - model: "openai/*" - - model_name: claude-3-5-sonnet-20240620 + api_base: os.environ/AZURE_AI_MISTRAL_API_BASE + api_key: os.environ/AZURE_AI_MISTRAL_API_KEY + model: azure_ai/Mistral-large-nmefg + - model_name: azure-ai-phi litellm_params: - model: gpt-3.5-turbo + api_base: os.environ/AZURE_AI_PHI_API_BASE + api_key: os.environ/AZURE_AI_PHI_API_KEY + model: azure_ai/Phi-3-medium-128k-instruct-fpmvj general_settings: diff --git a/litellm/tests/test_completion_cost.py b/litellm/tests/test_completion_cost.py index 1b4df0ecc08..5d30ccb0367 100644 --- a/litellm/tests/test_completion_cost.py +++ b/litellm/tests/test_completion_cost.py @@ -712,6 +712,79 @@ def test_vertex_ai_claude_completion_cost(): assert cost == predicted_cost +def test_vertex_ai_embedding_completion_cost(caplog): + """ + Relevant issue - https://github.com/BerriAI/litellm/issues/4630 + """ + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + text = "The quick brown fox jumps over the lazy dog." + input_tokens = litellm.token_counter( + model="vertex_ai/textembedding-gecko", text=text + ) + + model_info = litellm.get_model_info(model="vertex_ai/textembedding-gecko") + + print("\nExpected model info:\n{}\n\n".format(model_info)) + + expected_input_cost = input_tokens * model_info["input_cost_per_token"] + + ## CALCULATED COST + calculated_input_cost, calculated_output_cost = cost_per_token( + model="textembedding-gecko", + custom_llm_provider="vertex_ai", + prompt_tokens=input_tokens, + call_type="aembedding", + ) + + assert round(expected_input_cost, 6) == round(calculated_input_cost, 6) + print("expected_input_cost: {}".format(expected_input_cost)) + print("calculated_input_cost: {}".format(calculated_input_cost)) + + captured_logs = [rec.message for rec in caplog.records] + for item in captured_logs: + print("\nitem:{}\n".format(item)) + if ( + "litellm.litellm_core_utils.llm_cost_calc.google.cost_per_character(): Exception occured " + in item + ): + raise Exception("Error log raised for calculating embedding cost") + + +# def test_vertex_ai_embedding_completion_cost_e2e(): +# """ +# Relevant issue - https://github.com/BerriAI/litellm/issues/4630 +# """ +# from litellm.tests.test_amazing_vertex_completion import load_vertex_ai_credentials + +# load_vertex_ai_credentials() +# os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" +# litellm.model_cost = litellm.get_model_cost_map(url="") + +# text = "The quick brown fox jumps over the lazy dog." +# input_tokens = litellm.token_counter( +# model="vertex_ai/textembedding-gecko", text=text +# ) + +# model_info = litellm.get_model_info(model="vertex_ai/textembedding-gecko") + +# print("\nExpected model info:\n{}\n\n".format(model_info)) + +# expected_input_cost = input_tokens * model_info["input_cost_per_token"] + +# ## CALCULATED COST +# resp = litellm.embedding(model="textembedding-gecko", input=[text]) + +# calculated_input_cost = resp._hidden_params["response_cost"] + +# assert round(expected_input_cost, 6) == round(calculated_input_cost, 6) +# print("expected_input_cost: {}".format(expected_input_cost)) +# print("calculated_input_cost: {}".format(calculated_input_cost)) + +# assert False + + @pytest.mark.parametrize("sync_mode", [True, False]) @pytest.mark.asyncio async def test_completion_cost_hidden_params(sync_mode): From 1ba3fcc3fbafddc5b05a522ff82e9cc077a26f31 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 12:02:23 -0700 Subject: [PATCH 69/89] feat(utils.py): accept 'api_key' as param for validate_environment Closes https://github.com/BerriAI/litellm/issues/4375 --- litellm/tests/test_utils.py | 7 +++++++ litellm/utils.py | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_utils.py b/litellm/tests/test_utils.py index 8225b309dc5..db2d9ab5eea 100644 --- a/litellm/tests/test_utils.py +++ b/litellm/tests/test_utils.py @@ -258,6 +258,13 @@ def test_validate_environment_empty_model(): raise Exception() +def test_validate_environment_api_key(): + response_obj = validate_environment(model="gpt-3.5-turbo", api_key="sk-my-test-key") + assert ( + response_obj["keys_in_environment"] is True + ), f"Missing keys={response_obj['missing_keys']}" + + @mock.patch.dict(os.environ, {"OLLAMA_API_BASE": "foo"}, clear=True) def test_validate_environment_ollama(): for provider in ["ollama", "ollama_chat"]: diff --git a/litellm/utils.py b/litellm/utils.py index 39ddc02ac9b..56331c6cee7 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5048,12 +5048,15 @@ def create_proxy_transport_and_mounts(): return sync_proxy_mounts, async_proxy_mounts -def validate_environment(model: Optional[str] = None) -> dict: +def validate_environment( + model: Optional[str] = None, api_key: Optional[str] = None +) -> dict: """ Checks if the environment variables are valid for the given model. Args: model (Optional[str]): The name of the model. Defaults to None. + api_key (Optional[str]): If the user passed in an api key, of their own. Returns: dict: A dictionary containing the following keys: @@ -5329,6 +5332,13 @@ def validate_environment(model: Optional[str] = None) -> dict: keys_in_environment = True else: missing_keys.append("NLP_CLOUD_API_KEY") + + if api_key is not None: + new_missing_keys = [] + for key in missing_keys: + if "api_key" not in key.lower(): + new_missing_keys.append(key) + missing_keys = new_missing_keys return {"keys_in_environment": keys_in_environment, "missing_keys": missing_keys} From dd1048cb3519ccba4804d53a2805efbb44922981 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 12:11:50 -0700 Subject: [PATCH 70/89] fix(main.py): fix linting errors --- litellm/main.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index 949466642ca..bc40194b435 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -522,7 +522,7 @@ def mock_completion( ) return response if n is None: - model_response["choices"][0]["message"]["content"] = mock_response + model_response.choices[0].message.content = mock_response # type: ignore else: _all_choices = [] for i in range(n): @@ -533,12 +533,12 @@ def mock_completion( ), ) _all_choices.append(_choice) - model_response["choices"] = _all_choices - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.choices = _all_choices # type: ignore + model_response.created = int(time.time()) + model_response.model = model if mock_tool_calls: - model_response["choices"][0]["message"]["tool_calls"] = [ + model_response.choices[0].message.tool_calls = [ # type: ignore ChatCompletionMessageToolCall(**tool_call) for tool_call in mock_tool_calls ] @@ -2672,9 +2672,9 @@ def completion( """ string_response = response_json["data"][0]["output"][0] ## RESPONSE OBJECT - model_response["choices"][0]["message"]["content"] = string_response - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.choices[0].message.content = string_response # type: ignore + model_response.created = int(time.time()) + model_response.model = model response = model_response else: raise ValueError( From 341f88d1917db416b31f8fd09db8a3a85c561e30 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 12:59:42 -0700 Subject: [PATCH 71/89] fix supports vision --- litellm/proxy/proxy_config.yaml | 3 +++ litellm/types/utils.py | 1 + litellm/utils.py | 11 ++++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index 5fd4e5b62c2..762155ed987 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -4,6 +4,9 @@ model_list: model: openai/fake api_key: fake-key api_base: https://exampleopenaiendpoint-production.up.railway.app/ + - model_name: gemini-flash + litellm_params: + model: gemini/gemini-1.5-flash general_settings: master_key: sk-1234 diff --git a/litellm/types/utils.py b/litellm/types/utils.py index a90f93484f1..9c32fbf4d61 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -73,6 +73,7 @@ class ModelInfo(TypedDict, total=False): supported_openai_params: Required[Optional[List[str]]] supports_system_messages: Optional[bool] supports_response_schema: Optional[bool] + supports_vision: Optional[bool] class GenericStreamingChunk(TypedDict): diff --git a/litellm/utils.py b/litellm/utils.py index cf2c679a844..2dab185a3d5 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4829,6 +4829,7 @@ def get_model_info(model: str, custom_llm_provider: Optional[str] = None) -> Mod supports_response_schema=_model_info.get( "supports_response_schema", None ), + supports_vision=_model_info.get("supports_vision", None), ) except Exception: raise Exception( @@ -8126,7 +8127,7 @@ class CustomStreamWrapper: if chunk.startswith(self.complete_response): # Remove last_sent_chunk only if it appears at the start of the new chunk - chunk = chunk[len(self.complete_response):] + chunk = chunk[len(self.complete_response) :] self.complete_response += chunk return chunk @@ -10124,7 +10125,7 @@ def mock_completion_streaming_obj( model_response, mock_response, model, n: Optional[int] = None ): for i in range(0, len(mock_response), 3): - completion_obj = Delta(role="assistant", content=mock_response[i: i + 3]) + completion_obj = Delta(role="assistant", content=mock_response[i : i + 3]) if n is None: model_response.choices[0].delta = completion_obj else: @@ -10133,7 +10134,7 @@ def mock_completion_streaming_obj( _streaming_choice = litellm.utils.StreamingChoices( index=j, delta=litellm.utils.Delta( - role="assistant", content=mock_response[i: i + 3] + role="assistant", content=mock_response[i : i + 3] ), ) _all_choices.append(_streaming_choice) @@ -10145,7 +10146,7 @@ async def async_mock_completion_streaming_obj( model_response, mock_response, model, n: Optional[int] = None ): for i in range(0, len(mock_response), 3): - completion_obj = Delta(role="assistant", content=mock_response[i: i + 3]) + completion_obj = Delta(role="assistant", content=mock_response[i : i + 3]) if n is None: model_response.choices[0].delta = completion_obj else: @@ -10154,7 +10155,7 @@ async def async_mock_completion_streaming_obj( _streaming_choice = litellm.utils.StreamingChoices( index=j, delta=litellm.utils.Delta( - role="assistant", content=mock_response[i: i + 3] + role="assistant", content=mock_response[i : i + 3] ), ) _all_choices.append(_streaming_choice) From 46493303edc301a514daaa9755973e90dd36bc2d Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 13:04:18 -0700 Subject: [PATCH 72/89] test get mode info for gemini/gemini-1.5-flash --- litellm/tests/test_get_model_info.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/litellm/tests/test_get_model_info.py b/litellm/tests/test_get_model_info.py index 3fd6a6d22f6..687aa062f15 100644 --- a/litellm/tests/test_get_model_info.py +++ b/litellm/tests/test_get_model_info.py @@ -1,13 +1,16 @@ # What is this? ## Unit testing for the 'get_model_info()' function -import os, sys, traceback +import os +import sys +import traceback sys.path.insert( 0, os.path.abspath("../..") ) # Adds the parent directory to the system path +import pytest + import litellm from litellm import get_model_info -import pytest def test_get_model_info_simple_model_name(): @@ -37,3 +40,9 @@ def test_get_model_info_custom_llm_with_same_name_vllm(): pytest.fail("Expected get model info to fail for an unmapped model/provider") except Exception: pass + + +def test_get_model_info_shows_correct_supports_vision(): + info = litellm.get_model_info("gemini/gemini-1.5-flash") + print("info", info) + assert info["supports_vision"] is True From 6e9f0486189881988ccfb01fa258828f782ea4b8 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 13:18:36 -0700 Subject: [PATCH 73/89] fix: move to using pydantic obj for setting values --- .pre-commit-config.yaml | 12 +- litellm/llms/ai21.py | 33 +- litellm/llms/aleph_alpha.py | 19 +- litellm/llms/anthropic.py | 4 +- litellm/llms/anthropic_text.py | 28 +- litellm/llms/baseten.py | 42 +- litellm/llms/bedrock.py | 22 +- litellm/llms/bedrock_httpx.py | 24 +- litellm/llms/clarifai.py | 56 ++- litellm/llms/cloudflare.py | 24 +- litellm/llms/cohere.py | 38 +- litellm/llms/cohere_chat.py | 4 +- litellm/llms/databricks.py | 36 +- litellm/llms/gemini.py | 612 ++++++++++++++-------------- litellm/llms/huggingface_restapi.py | 75 ++-- litellm/llms/maritalk.py | 22 +- litellm/llms/nlp_cloud.py | 15 +- litellm/llms/ollama.py | 116 +++--- litellm/llms/ollama_chat.py | 88 ++-- litellm/llms/oobabooga.py | 35 +- litellm/llms/palm.py | 16 +- litellm/llms/petals.py | 20 +- litellm/llms/predibase.py | 16 +- litellm/llms/replicate.py | 6 +- litellm/llms/sagemaker.py | 55 +-- litellm/llms/together_ai.py | 404 +++++++++--------- litellm/llms/vertex_ai.py | 50 ++- litellm/llms/vertex_ai_anthropic.py | 4 +- litellm/llms/vllm.py | 24 +- litellm/llms/watsonx.py | 4 +- 30 files changed, 1018 insertions(+), 886 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a33473b7246..d429bc6b8c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: local hooks: - - id: mypy - name: mypy - entry: python3 -m mypy --ignore-missing-imports - language: system - types: [python] - files: ^litellm/ + # - id: mypy + # name: mypy + # entry: python3 -m mypy --ignore-missing-imports + # language: system + # types: [python] + # files: ^litellm/ - id: isort name: isort entry: isort diff --git a/litellm/llms/ai21.py b/litellm/llms/ai21.py index a39a83f1575..e65a8109938 100644 --- a/litellm/llms/ai21.py +++ b/litellm/llms/ai21.py @@ -1,11 +1,16 @@ -import os, types, traceback import json +import os +import time # type: ignore +import traceback +import types from enum import Enum -import requests # type: ignore -import time, httpx # type: ignore from typing import Callable, Optional -from litellm.utils import ModelResponse, Choices, Message + +import httpx +import requests # type: ignore + import litellm +from litellm.utils import Choices, Message, ModelResponse class AI21Error(Exception): @@ -185,7 +190,7 @@ def completion( message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list # type: ignore except Exception as e: raise AI21Error( message=traceback.format_exc(), status_code=response.status_code @@ -197,13 +202,17 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model - model_response["usage"] = { - "prompt_tokens": prompt_tokens, - "completion_tokens": completion_tokens, - "total_tokens": prompt_tokens + completion_tokens, - } + model_response.created = int(time.time()) + model_response.model = model + setattr( + model_response, + "usage", + litellm.Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), + ) return model_response diff --git a/litellm/llms/aleph_alpha.py b/litellm/llms/aleph_alpha.py index 7edd11964b6..163e96fde02 100644 --- a/litellm/llms/aleph_alpha.py +++ b/litellm/llms/aleph_alpha.py @@ -1,12 +1,15 @@ -import os, types import json -from enum import Enum -import requests # type: ignore +import os import time +import types +from enum import Enum from typing import Callable, Optional -import litellm -from litellm.utils import ModelResponse, Choices, Message, Usage + import httpx # type: ignore +import requests # type: ignore + +import litellm +from litellm.utils import Choices, Message, ModelResponse, Usage class AlephAlphaError(Exception): @@ -275,7 +278,7 @@ def completion( message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list # type: ignore except: raise AlephAlphaError( message=json.dumps(completion_response), @@ -291,8 +294,8 @@ def completion( ) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/anthropic.py b/litellm/llms/anthropic.py index ca93a85b71d..733cce1e011 100644 --- a/litellm/llms/anthropic.py +++ b/litellm/llms/anthropic.py @@ -607,8 +607,8 @@ class AnthropicChatCompletion(BaseLLM): completion_tokens = completion_response["usage"]["output_tokens"] total_tokens = prompt_tokens + completion_tokens - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/anthropic_text.py b/litellm/llms/anthropic_text.py index 0093d9f3532..d20e49daf1e 100644 --- a/litellm/llms/anthropic_text.py +++ b/litellm/llms/anthropic_text.py @@ -1,15 +1,19 @@ -import os, types import json -from enum import Enum -import requests +import os import time +import types +from enum import Enum from typing import Callable, Optional -from litellm.utils import ModelResponse, Usage, CustomStreamWrapper -import litellm -from .prompt_templates.factory import prompt_factory, custom_prompt + import httpx -from .base import BaseLLM +import requests + +import litellm from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler +from litellm.utils import CustomStreamWrapper, ModelResponse, Usage + +from .base import BaseLLM +from .prompt_templates.factory import custom_prompt, prompt_factory class AnthropicConstants(Enum): @@ -117,9 +121,9 @@ class AnthropicTextCompletion(BaseLLM): ) else: if len(completion_response["completion"]) > 0: - model_response["choices"][0]["message"]["content"] = ( - completion_response["completion"] - ) + model_response.choices[0].message.content = completion_response[ # type: ignore + "completion" + ] model_response.choices[0].finish_reason = completion_response["stop_reason"] ## CALCULATING USAGE @@ -130,8 +134,8 @@ class AnthropicTextCompletion(BaseLLM): encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) ##[TODO] use the anthropic tokenizer here - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/baseten.py b/litellm/llms/baseten.py index 643dae5304e..d856efc9ab9 100644 --- a/litellm/llms/baseten.py +++ b/litellm/llms/baseten.py @@ -1,9 +1,11 @@ -import os import json -from enum import Enum -import requests # type: ignore +import os import time +from enum import Enum from typing import Callable + +import requests # type: ignore + from litellm.utils import ModelResponse, Usage @@ -106,28 +108,32 @@ def completion( and "data" in completion_response["model_output"] and isinstance(completion_response["model_output"]["data"], list) ): - model_response["choices"][0]["message"]["content"] = ( - completion_response["model_output"]["data"][0] - ) + model_response.choices[0].message.content = completion_response[ # type: ignore + "model_output" + ][ + "data" + ][ + 0 + ] elif isinstance(completion_response["model_output"], str): - model_response["choices"][0]["message"]["content"] = ( - completion_response["model_output"] - ) + model_response.choices[0].message.content = completion_response[ # type: ignore + "model_output" + ] elif "completion" in completion_response and isinstance( completion_response["completion"], str ): - model_response["choices"][0]["message"]["content"] = ( - completion_response["completion"] - ) + model_response.choices[0].message.content = completion_response[ # type: ignore + "completion" + ] elif isinstance(completion_response, list) and len(completion_response) > 0: if "generated_text" not in completion_response: raise BasetenError( message=f"Unable to parse response. Original response: {response.text}", status_code=response.status_code, ) - model_response["choices"][0]["message"]["content"] = ( - completion_response[0]["generated_text"] - ) + model_response.choices[0].message.content = completion_response[0][ # type: ignore + "generated_text" + ] ## GETTING LOGPROBS if ( "details" in completion_response[0] @@ -139,7 +145,7 @@ def completion( sum_logprob = 0 for token in completion_response[0]["details"]["tokens"]: sum_logprob += token["logprob"] - model_response["choices"][0]["message"]._logprobs = sum_logprob + model_response.choices[0].logprobs = sum_logprob else: raise BasetenError( message=f"Unable to parse response. Original response: {response.text}", @@ -152,8 +158,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"]["content"]) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/bedrock.py b/litellm/llms/bedrock.py index a8c47b3b918..e12b656ed1f 100644 --- a/litellm/llms/bedrock.py +++ b/litellm/llms/bedrock.py @@ -1122,7 +1122,7 @@ def completion( logging_obj=logging_obj, ) - model_response["finish_reason"] = map_finish_reason( + model_response.choices[0].finish_reason = map_finish_reason( response_body["stop_reason"] ) _usage = litellm.Usage( @@ -1134,14 +1134,16 @@ def completion( setattr(model_response, "usage", _usage) else: outputText = response_body["completion"] - model_response["finish_reason"] = response_body["stop_reason"] + model_response.choices[0].finish_reason = response_body["stop_reason"] elif provider == "cohere": outputText = response_body["generations"][0]["text"] elif provider == "meta": outputText = response_body["generation"] elif provider == "mistral": outputText = response_body["outputs"][0]["text"] - model_response["finish_reason"] = response_body["outputs"][0]["stop_reason"] + model_response.choices[0].finish_reason = response_body["outputs"][0][ + "stop_reason" + ] else: # amazon titan outputText = response_body.get("results")[0].get("outputText") @@ -1160,7 +1162,7 @@ def completion( and getattr(model_response.choices[0].message, "tool_calls", None) is None ): - model_response["choices"][0]["message"]["content"] = outputText + model_response.choices[0].message.content = outputText elif ( hasattr(model_response.choices[0], "message") and getattr(model_response.choices[0].message, "tool_calls", None) @@ -1199,8 +1201,8 @@ def completion( ) setattr(model_response, "usage", usage) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model model_response._hidden_params["region_name"] = client.meta.region_name print_verbose(f"model_response._hidden_params: {model_response._hidden_params}") @@ -1323,9 +1325,9 @@ def _embedding_func_single( def embedding( model: str, input: Union[list, str], + model_response: litellm.EmbeddingResponse, api_key: Optional[str] = None, logging_obj=None, - model_response=None, optional_params=None, encoding=None, ): @@ -1391,9 +1393,9 @@ def embedding( "embedding": embedding, } ) - model_response["object"] = "list" - model_response["data"] = embedding_response - model_response["model"] = model + model_response.object = "list" + model_response.data = embedding_response + model_response.model = model input_tokens = 0 input_str = "".join(input) diff --git a/litellm/llms/bedrock_httpx.py b/litellm/llms/bedrock_httpx.py index 33d9203381b..f2700495f8d 100644 --- a/litellm/llms/bedrock_httpx.py +++ b/litellm/llms/bedrock_httpx.py @@ -521,7 +521,7 @@ class BedrockLLM(BaseLLM): outputText = completion_response["text"] # type: ignore elif "generations" in completion_response: outputText = completion_response["generations"][0]["text"] - model_response["finish_reason"] = map_finish_reason( + model_response.choices[0].finish_reason = map_finish_reason( completion_response["generations"][0]["finish_reason"] ) elif provider == "anthropic": @@ -625,7 +625,7 @@ class BedrockLLM(BaseLLM): logging_obj=logging_obj, ) - model_response["finish_reason"] = map_finish_reason( + model_response.choices[0].finish_reason = map_finish_reason( completion_response.get("stop_reason", "") ) _usage = litellm.Usage( @@ -638,7 +638,9 @@ class BedrockLLM(BaseLLM): else: outputText = completion_response["completion"] - model_response["finish_reason"] = completion_response["stop_reason"] + model_response.choices[0].finish_reason = completion_response[ + "stop_reason" + ] elif provider == "ai21": outputText = ( completion_response.get("completions")[0].get("data").get("text") @@ -647,9 +649,9 @@ class BedrockLLM(BaseLLM): outputText = completion_response["generation"] elif provider == "mistral": outputText = completion_response["outputs"][0]["text"] - model_response["finish_reason"] = completion_response["outputs"][0][ - "stop_reason" - ] + model_response.choices[0].finish_reason = completion_response[ + "outputs" + ][0]["stop_reason"] else: # amazon titan outputText = completion_response.get("results")[0].get("outputText") except Exception as e: @@ -667,7 +669,7 @@ class BedrockLLM(BaseLLM): and getattr(model_response.choices[0].message, "tool_calls", None) is None ): - model_response["choices"][0]["message"]["content"] = outputText + model_response.choices[0].message.content = outputText elif ( hasattr(model_response.choices[0], "message") and getattr(model_response.choices[0].message, "tool_calls", None) @@ -723,8 +725,8 @@ class BedrockLLM(BaseLLM): ) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -1446,8 +1448,8 @@ class BedrockConverseLLM(BaseLLM): message=litellm.Message(**chat_completion_message), ) ] - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=input_tokens, completion_tokens=output_tokens, diff --git a/litellm/llms/clarifai.py b/litellm/llms/clarifai.py index 785a7ad38be..fb517d98dee 100644 --- a/litellm/llms/clarifai.py +++ b/litellm/llms/clarifai.py @@ -1,13 +1,18 @@ -import os, types, traceback import json -import requests +import os import time +import traceback +import types from typing import Callable, Optional -from litellm.utils import ModelResponse, Usage, Choices, Message, CustomStreamWrapper -import litellm + import httpx +import requests + +import litellm from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler -from .prompt_templates.factory import prompt_factory, custom_prompt +from litellm.utils import Choices, CustomStreamWrapper, Message, ModelResponse, Usage + +from .prompt_templates.factory import custom_prompt, prompt_factory class ClarifaiError(Exception): @@ -87,7 +92,14 @@ def completions_to_model(payload): def process_response( - model, prompt, response, model_response, api_key, data, encoding, logging_obj + model, + prompt, + response, + model_response: litellm.ModelResponse, + api_key, + data, + encoding, + logging_obj, ): logging_obj.post_call( input=prompt, @@ -116,7 +128,7 @@ def process_response( message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list except Exception as e: raise ClarifaiError( @@ -128,11 +140,15 @@ def process_response( completion_tokens = len( encoding.encode(model_response["choices"][0]["message"].get("content")) ) - model_response["model"] = model - model_response["usage"] = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + model_response.model = model + setattr( + model_response, + "usage", + Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response @@ -202,7 +218,7 @@ async def async_completion( message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list # type: ignore except Exception as e: raise ClarifaiError( @@ -214,11 +230,15 @@ async def async_completion( completion_tokens = len( encoding.encode(model_response["choices"][0]["message"].get("content")) ) - model_response["model"] = model - model_response["usage"] = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + model_response.model = model + setattr( + model_response, + "usage", + Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response diff --git a/litellm/llms/cloudflare.py b/litellm/llms/cloudflare.py index 5a24b3b443f..516b490f442 100644 --- a/litellm/llms/cloudflare.py +++ b/litellm/llms/cloudflare.py @@ -1,13 +1,17 @@ -import os, types import json -from enum import Enum -import requests # type: ignore +import os import time +import types +from enum import Enum from typing import Callable, Optional -import litellm + import httpx # type: ignore +import requests # type: ignore + +import litellm from litellm.utils import ModelResponse, Usage -from .prompt_templates.factory import prompt_factory, custom_prompt + +from .prompt_templates.factory import custom_prompt, prompt_factory class CloudflareError(Exception): @@ -147,9 +151,9 @@ def completion( ) completion_response = response.json() - model_response["choices"][0]["message"]["content"] = completion_response[ - "result" - ]["response"] + model_response.choices[0].message.content = completion_response["result"][ # type: ignore + "response" + ] ## CALCULATING USAGE print_verbose( @@ -160,8 +164,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = "cloudflare/" + model + model_response.created = int(time.time()) + model_response.model = "cloudflare/" + model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/cohere.py b/litellm/llms/cohere.py index 14a66b54ac9..d946a8ddeef 100644 --- a/litellm/llms/cohere.py +++ b/litellm/llms/cohere.py @@ -1,12 +1,16 @@ -import os, types import json +import os +import time +import traceback +import types from enum import Enum -import requests # type: ignore -import time, traceback from typing import Callable, Optional -from litellm.utils import ModelResponse, Choices, Message, Usage -import litellm + import httpx # type: ignore +import requests # type: ignore + +import litellm +from litellm.utils import Choices, Message, ModelResponse, Usage class CohereError(Exception): @@ -117,7 +121,7 @@ class CohereConfig: def validate_environment(api_key): headers = { - "Request-Source":"unspecified:litellm", + "Request-Source": "unspecified:litellm", "accept": "application/json", "content-type": "application/json", } @@ -219,7 +223,7 @@ def completion( message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list # type: ignore except Exception as e: raise CohereError( message=response.text, status_code=response.status_code @@ -231,8 +235,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -245,9 +249,9 @@ def completion( def embedding( model: str, input: list, + model_response: litellm.EmbeddingResponse, api_key: Optional[str] = None, logging_obj=None, - model_response=None, encoding=None, optional_params=None, ): @@ -294,14 +298,18 @@ def embedding( output_data.append( {"object": "embedding", "index": idx, "embedding": embedding} ) - model_response["object"] = "list" - model_response["data"] = output_data - model_response["model"] = model + model_response.object = "list" + model_response.data = output_data + model_response.model = model input_tokens = 0 for text in input: input_tokens += len(encoding.encode(text)) - model_response["usage"] = Usage( - prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens + setattr( + model_response, + "usage", + Usage( + prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens + ), ) return model_response diff --git a/litellm/llms/cohere_chat.py b/litellm/llms/cohere_chat.py index 1b3aa8405d0..fe209294623 100644 --- a/litellm/llms/cohere_chat.py +++ b/litellm/llms/cohere_chat.py @@ -305,8 +305,8 @@ def completion( prompt_tokens = billed_units.get("input_tokens", 0) completion_tokens = billed_units.get("output_tokens", 0) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/databricks.py b/litellm/llms/databricks.py index 1ab09246b06..88fa58abec2 100644 --- a/litellm/llms/databricks.py +++ b/litellm/llms/databricks.py @@ -1,26 +1,26 @@ # What is this? ## Handler file for databricks API https://docs.databricks.com/en/machine-learning/foundation-models/api-reference.html#chat-request -from functools import partial -import os, types +import copy import json -from enum import Enum -import requests, copy # type: ignore +import os import time -from typing import Callable, Optional, List, Union, Tuple, Literal -from litellm.utils import ( - ModelResponse, - Usage, - CustomStreamWrapper, - EmbeddingResponse, -) -from litellm.litellm_core_utils.core_helpers import map_finish_reason -import litellm -from .prompt_templates.factory import prompt_factory, custom_prompt -from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler -from .base import BaseLLM +import types +from enum import Enum +from functools import partial +from typing import Callable, List, Literal, Optional, Tuple, Union + import httpx # type: ignore +import requests # type: ignore + +import litellm +from litellm.litellm_core_utils.core_helpers import map_finish_reason +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.types.llms.databricks import GenericStreamingChunk from litellm.types.utils import ProviderField +from litellm.utils import CustomStreamWrapper, EmbeddingResponse, ModelResponse, Usage + +from .base import BaseLLM +from .prompt_templates.factory import custom_prompt, prompt_factory class DatabricksError(Exception): @@ -354,8 +354,8 @@ class DatabricksChatCompletion(BaseLLM): completion_tokens = completion_response["usage"]["output_tokens"] total_tokens = prompt_tokens + completion_tokens - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/gemini.py b/litellm/llms/gemini.py index f48c4e29ea6..3ce63e93fe0 100644 --- a/litellm/llms/gemini.py +++ b/litellm/llms/gemini.py @@ -1,7 +1,7 @@ -#################################### -######### DEPRECATED FILE ########## -#################################### -# logic moved to `vertex_httpx.py` # +# #################################### +# ######### DEPRECATED FILE ########## +# #################################### +# # logic moved to `vertex_httpx.py` # import copy import time @@ -92,332 +92,332 @@ class GeminiConfig: } -class TextStreamer: - """ - A class designed to return an async stream from AsyncGenerateContentResponse object. - """ +# class TextStreamer: +# """ +# A class designed to return an async stream from AsyncGenerateContentResponse object. +# """ - def __init__(self, response): - self.response = response - self._aiter = self.response.__aiter__() +# def __init__(self, response): +# self.response = response +# self._aiter = self.response.__aiter__() - async def __aiter__(self): - while True: - try: - # This will manually advance the async iterator. - # In the case the next object doesn't exists, __anext__() will simply raise a StopAsyncIteration exception - next_object = await self._aiter.__anext__() - yield next_object - except StopAsyncIteration: - # After getting all items from the async iterator, stop iterating - break +# async def __aiter__(self): +# while True: +# try: +# # This will manually advance the async iterator. +# # In the case the next object doesn't exists, __anext__() will simply raise a StopAsyncIteration exception +# next_object = await self._aiter.__anext__() +# yield next_object +# except StopAsyncIteration: +# # After getting all items from the async iterator, stop iterating +# break -def supports_system_instruction(): - import google.generativeai as genai +# def supports_system_instruction(): +# import google.generativeai as genai - gemini_pkg_version = Version(genai.__version__) - return gemini_pkg_version >= Version("0.5.0") +# gemini_pkg_version = Version(genai.__version__) +# return gemini_pkg_version >= Version("0.5.0") -def completion( - model: str, - messages: list, - model_response: ModelResponse, - print_verbose: Callable, - api_key, - encoding, - logging_obj, - custom_prompt_dict: dict, - acompletion: bool = False, - optional_params=None, - litellm_params=None, - logger_fn=None, -): - try: - import google.generativeai as genai # type: ignore - except: - raise Exception( - "Importing google.generativeai failed, please run 'pip install -q google-generativeai" - ) - genai.configure(api_key=api_key) - system_prompt = "" - if model in custom_prompt_dict: - # check if the model has a registered custom prompt - model_prompt_details = custom_prompt_dict[model] - prompt = custom_prompt( - role_dict=model_prompt_details["roles"], - initial_prompt_value=model_prompt_details["initial_prompt_value"], - final_prompt_value=model_prompt_details["final_prompt_value"], - messages=messages, - ) - else: - system_prompt, messages = get_system_prompt(messages=messages) - prompt = prompt_factory( - model=model, messages=messages, custom_llm_provider="gemini" - ) +# def completion( +# model: str, +# messages: list, +# model_response: ModelResponse, +# print_verbose: Callable, +# api_key, +# encoding, +# logging_obj, +# custom_prompt_dict: dict, +# acompletion: bool = False, +# optional_params=None, +# litellm_params=None, +# logger_fn=None, +# ): +# try: +# import google.generativeai as genai # type: ignore +# except: +# raise Exception( +# "Importing google.generativeai failed, please run 'pip install -q google-generativeai" +# ) +# genai.configure(api_key=api_key) +# system_prompt = "" +# if model in custom_prompt_dict: +# # check if the model has a registered custom prompt +# model_prompt_details = custom_prompt_dict[model] +# prompt = custom_prompt( +# role_dict=model_prompt_details["roles"], +# initial_prompt_value=model_prompt_details["initial_prompt_value"], +# final_prompt_value=model_prompt_details["final_prompt_value"], +# messages=messages, +# ) +# else: +# system_prompt, messages = get_system_prompt(messages=messages) +# prompt = prompt_factory( +# model=model, messages=messages, custom_llm_provider="gemini" +# ) - ## Load Config - inference_params = copy.deepcopy(optional_params) - stream = inference_params.pop("stream", None) +# ## Load Config +# inference_params = copy.deepcopy(optional_params) +# stream = inference_params.pop("stream", None) - # Handle safety settings - safety_settings_param = inference_params.pop("safety_settings", None) - safety_settings = None - if safety_settings_param: - safety_settings = [ - genai.types.SafetySettingDict(x) for x in safety_settings_param - ] +# # Handle safety settings +# safety_settings_param = inference_params.pop("safety_settings", None) +# safety_settings = None +# if safety_settings_param: +# safety_settings = [ +# genai.types.SafetySettingDict(x) for x in safety_settings_param +# ] - config = litellm.GeminiConfig.get_config() - for k, v in config.items(): - if ( - k not in inference_params - ): # completion(top_k=3) > gemini_config(top_k=3) <- allows for dynamic variables to be passed in - inference_params[k] = v +# config = litellm.GeminiConfig.get_config() +# for k, v in config.items(): +# if ( +# k not in inference_params +# ): # completion(top_k=3) > gemini_config(top_k=3) <- allows for dynamic variables to be passed in +# inference_params[k] = v - ## LOGGING - logging_obj.pre_call( - input=prompt, - api_key="", - additional_args={ - "complete_input_dict": { - "inference_params": inference_params, - "system_prompt": system_prompt, - } - }, - ) - ## COMPLETION CALL - try: - _params = {"model_name": "models/{}".format(model)} - _system_instruction = supports_system_instruction() - if _system_instruction and len(system_prompt) > 0: - _params["system_instruction"] = system_prompt - _model = genai.GenerativeModel(**_params) - if stream is True: - if acompletion is True: +# ## LOGGING +# logging_obj.pre_call( +# input=prompt, +# api_key="", +# additional_args={ +# "complete_input_dict": { +# "inference_params": inference_params, +# "system_prompt": system_prompt, +# } +# }, +# ) +# ## COMPLETION CALL +# try: +# _params = {"model_name": "models/{}".format(model)} +# _system_instruction = supports_system_instruction() +# if _system_instruction and len(system_prompt) > 0: +# _params["system_instruction"] = system_prompt +# _model = genai.GenerativeModel(**_params) +# if stream is True: +# if acompletion is True: - async def async_streaming(): - try: - response = await _model.generate_content_async( - contents=prompt, - generation_config=genai.types.GenerationConfig( - **inference_params - ), - safety_settings=safety_settings, - stream=True, - ) +# async def async_streaming(): +# try: +# response = await _model.generate_content_async( +# contents=prompt, +# generation_config=genai.types.GenerationConfig( +# **inference_params +# ), +# safety_settings=safety_settings, +# stream=True, +# ) - response = litellm.CustomStreamWrapper( - TextStreamer(response), - model, - custom_llm_provider="gemini", - logging_obj=logging_obj, - ) - return response - except Exception as e: - raise GeminiError(status_code=500, message=str(e)) +# response = litellm.CustomStreamWrapper( +# TextStreamer(response), +# model, +# custom_llm_provider="gemini", +# logging_obj=logging_obj, +# ) +# return response +# except Exception as e: +# raise GeminiError(status_code=500, message=str(e)) - return async_streaming() - response = _model.generate_content( - contents=prompt, - generation_config=genai.types.GenerationConfig(**inference_params), - safety_settings=safety_settings, - stream=True, - ) - return response - elif acompletion == True: - return async_completion( - _model=_model, - model=model, - prompt=prompt, - inference_params=inference_params, - safety_settings=safety_settings, - logging_obj=logging_obj, - print_verbose=print_verbose, - model_response=model_response, - messages=messages, - encoding=encoding, - ) - else: - params = { - "contents": prompt, - "generation_config": genai.types.GenerationConfig(**inference_params), - "safety_settings": safety_settings, - } - response = _model.generate_content(**params) - except Exception as e: - raise GeminiError( - message=str(e), - status_code=500, - ) +# return async_streaming() +# response = _model.generate_content( +# contents=prompt, +# generation_config=genai.types.GenerationConfig(**inference_params), +# safety_settings=safety_settings, +# stream=True, +# ) +# return response +# elif acompletion == True: +# return async_completion( +# _model=_model, +# model=model, +# prompt=prompt, +# inference_params=inference_params, +# safety_settings=safety_settings, +# logging_obj=logging_obj, +# print_verbose=print_verbose, +# model_response=model_response, +# messages=messages, +# encoding=encoding, +# ) +# else: +# params = { +# "contents": prompt, +# "generation_config": genai.types.GenerationConfig(**inference_params), +# "safety_settings": safety_settings, +# } +# response = _model.generate_content(**params) +# except Exception as e: +# raise GeminiError( +# message=str(e), +# status_code=500, +# ) - ## LOGGING - logging_obj.post_call( - input=prompt, - api_key="", - original_response=response, - additional_args={"complete_input_dict": {}}, - ) - print_verbose(f"raw model_response: {response}") - ## RESPONSE OBJECT - completion_response = response - try: - choices_list = [] - for idx, item in enumerate(completion_response.candidates): - if len(item.content.parts) > 0: - message_obj = Message(content=item.content.parts[0].text) - else: - message_obj = Message(content=None) - choice_obj = Choices(index=idx, message=message_obj) - choices_list.append(choice_obj) - model_response["choices"] = choices_list - except Exception as e: - verbose_logger.error("LiteLLM.gemini.py: Exception occured - {}".format(str(e))) - verbose_logger.debug(traceback.format_exc()) - raise GeminiError( - message=traceback.format_exc(), status_code=response.status_code - ) +# ## LOGGING +# logging_obj.post_call( +# input=prompt, +# api_key="", +# original_response=response, +# additional_args={"complete_input_dict": {}}, +# ) +# print_verbose(f"raw model_response: {response}") +# ## RESPONSE OBJECT +# completion_response = response +# try: +# choices_list = [] +# for idx, item in enumerate(completion_response.candidates): +# if len(item.content.parts) > 0: +# message_obj = Message(content=item.content.parts[0].text) +# else: +# message_obj = Message(content=None) +# choice_obj = Choices(index=idx, message=message_obj) +# choices_list.append(choice_obj) +# model_response.choices = choices_list +# except Exception as e: +# verbose_logger.error("LiteLLM.gemini.py: Exception occured - {}".format(str(e))) +# verbose_logger.debug(traceback.format_exc()) +# raise GeminiError( +# message=traceback.format_exc(), status_code=response.status_code +# ) - try: - completion_response = model_response["choices"][0]["message"].get("content") - if completion_response is None: - raise Exception - except: - original_response = f"response: {response}" - if hasattr(response, "candidates"): - original_response = f"response: {response.candidates}" - if "SAFETY" in original_response: - original_response += ( - "\nThe candidate content was flagged for safety reasons." - ) - elif "RECITATION" in original_response: - original_response += ( - "\nThe candidate content was flagged for recitation reasons." - ) - raise GeminiError( - status_code=400, - message=f"No response received. Original response - {original_response}", - ) +# try: +# completion_response = model_response["choices"][0]["message"].get("content") +# if completion_response is None: +# raise Exception +# except: +# original_response = f"response: {response}" +# if hasattr(response, "candidates"): +# original_response = f"response: {response.candidates}" +# if "SAFETY" in original_response: +# original_response += ( +# "\nThe candidate content was flagged for safety reasons." +# ) +# elif "RECITATION" in original_response: +# original_response += ( +# "\nThe candidate content was flagged for recitation reasons." +# ) +# raise GeminiError( +# status_code=400, +# message=f"No response received. Original response - {original_response}", +# ) - ## CALCULATING USAGE - prompt_str = "" - for m in messages: - if isinstance(m["content"], str): - prompt_str += m["content"] - elif isinstance(m["content"], list): - for content in m["content"]: - if content["type"] == "text": - prompt_str += content["text"] - prompt_tokens = len(encoding.encode(prompt_str)) - completion_tokens = len( - encoding.encode(model_response["choices"][0]["message"].get("content", "")) - ) +# ## CALCULATING USAGE +# prompt_str = "" +# for m in messages: +# if isinstance(m["content"], str): +# prompt_str += m["content"] +# elif isinstance(m["content"], list): +# for content in m["content"]: +# if content["type"] == "text": +# prompt_str += content["text"] +# prompt_tokens = len(encoding.encode(prompt_str)) +# completion_tokens = len( +# encoding.encode(model_response["choices"][0]["message"].get("content", "")) +# ) - model_response["created"] = int(time.time()) - model_response["model"] = "gemini/" + model - usage = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, - ) - setattr(model_response, "usage", usage) - return model_response +# model_response.created = int(time.time()) +# model_response.model = "gemini/" + model +# usage = Usage( +# prompt_tokens=prompt_tokens, +# completion_tokens=completion_tokens, +# total_tokens=prompt_tokens + completion_tokens, +# ) +# setattr(model_response, "usage", usage) +# return model_response -async def async_completion( - _model, - model, - prompt, - inference_params, - safety_settings, - logging_obj, - print_verbose, - model_response, - messages, - encoding, -): - import google.generativeai as genai # type: ignore +# async def async_completion( +# _model, +# model, +# prompt, +# inference_params, +# safety_settings, +# logging_obj, +# print_verbose, +# model_response, +# messages, +# encoding, +# ): +# import google.generativeai as genai # type: ignore - response = await _model.generate_content_async( - contents=prompt, - generation_config=genai.types.GenerationConfig(**inference_params), - safety_settings=safety_settings, - ) +# response = await _model.generate_content_async( +# contents=prompt, +# generation_config=genai.types.GenerationConfig(**inference_params), +# safety_settings=safety_settings, +# ) - ## LOGGING - logging_obj.post_call( - input=prompt, - api_key="", - original_response=response, - additional_args={"complete_input_dict": {}}, - ) - print_verbose(f"raw model_response: {response}") - ## RESPONSE OBJECT - completion_response = response - try: - choices_list = [] - for idx, item in enumerate(completion_response.candidates): - if len(item.content.parts) > 0: - message_obj = Message(content=item.content.parts[0].text) - else: - message_obj = Message(content=None) - choice_obj = Choices(index=idx, message=message_obj) - choices_list.append(choice_obj) - model_response["choices"] = choices_list - except Exception as e: - verbose_logger.error("LiteLLM.gemini.py: Exception occured - {}".format(str(e))) - verbose_logger.debug(traceback.format_exc()) - raise GeminiError( - message=traceback.format_exc(), status_code=response.status_code - ) +# ## LOGGING +# logging_obj.post_call( +# input=prompt, +# api_key="", +# original_response=response, +# additional_args={"complete_input_dict": {}}, +# ) +# print_verbose(f"raw model_response: {response}") +# ## RESPONSE OBJECT +# completion_response = response +# try: +# choices_list = [] +# for idx, item in enumerate(completion_response.candidates): +# if len(item.content.parts) > 0: +# message_obj = Message(content=item.content.parts[0].text) +# else: +# message_obj = Message(content=None) +# choice_obj = Choices(index=idx, message=message_obj) +# choices_list.append(choice_obj) +# model_response["choices"] = choices_list +# except Exception as e: +# verbose_logger.error("LiteLLM.gemini.py: Exception occured - {}".format(str(e))) +# verbose_logger.debug(traceback.format_exc()) +# raise GeminiError( +# message=traceback.format_exc(), status_code=response.status_code +# ) - try: - completion_response = model_response["choices"][0]["message"].get("content") - if completion_response is None: - raise Exception - except: - original_response = f"response: {response}" - if hasattr(response, "candidates"): - original_response = f"response: {response.candidates}" - if "SAFETY" in original_response: - original_response += ( - "\nThe candidate content was flagged for safety reasons." - ) - elif "RECITATION" in original_response: - original_response += ( - "\nThe candidate content was flagged for recitation reasons." - ) - raise GeminiError( - status_code=400, - message=f"No response received. Original response - {original_response}", - ) +# try: +# completion_response = model_response["choices"][0]["message"].get("content") +# if completion_response is None: +# raise Exception +# except: +# original_response = f"response: {response}" +# if hasattr(response, "candidates"): +# original_response = f"response: {response.candidates}" +# if "SAFETY" in original_response: +# original_response += ( +# "\nThe candidate content was flagged for safety reasons." +# ) +# elif "RECITATION" in original_response: +# original_response += ( +# "\nThe candidate content was flagged for recitation reasons." +# ) +# raise GeminiError( +# status_code=400, +# message=f"No response received. Original response - {original_response}", +# ) - ## CALCULATING USAGE - prompt_str = "" - for m in messages: - if isinstance(m["content"], str): - prompt_str += m["content"] - elif isinstance(m["content"], list): - for content in m["content"]: - if content["type"] == "text": - prompt_str += content["text"] - prompt_tokens = len(encoding.encode(prompt_str)) - completion_tokens = len( - encoding.encode(model_response["choices"][0]["message"].get("content", "")) - ) +# ## CALCULATING USAGE +# prompt_str = "" +# for m in messages: +# if isinstance(m["content"], str): +# prompt_str += m["content"] +# elif isinstance(m["content"], list): +# for content in m["content"]: +# if content["type"] == "text": +# prompt_str += content["text"] +# prompt_tokens = len(encoding.encode(prompt_str)) +# completion_tokens = len( +# encoding.encode(model_response["choices"][0]["message"].get("content", "")) +# ) - model_response["created"] = int(time.time()) - model_response["model"] = "gemini/" + model - usage = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, - ) - model_response.usage = usage - return model_response +# model_response["created"] = int(time.time()) +# model_response["model"] = "gemini/" + model +# usage = Usage( +# prompt_tokens=prompt_tokens, +# completion_tokens=completion_tokens, +# total_tokens=prompt_tokens + completion_tokens, +# ) +# model_response.usage = usage +# return model_response -def embedding(): - # logic for parsing in - calling - parsing out model embedding calls - pass +# def embedding(): +# # logic for parsing in - calling - parsing out model embedding calls +# pass diff --git a/litellm/llms/huggingface_restapi.py b/litellm/llms/huggingface_restapi.py index c54dba75f16..8b755e2bb70 100644 --- a/litellm/llms/huggingface_restapi.py +++ b/litellm/llms/huggingface_restapi.py @@ -1,17 +1,22 @@ ## Uses the huggingface text generation inference API -import os, copy, types -import json -from enum import Enum -import httpx, requests -from .base import BaseLLM -import time -import litellm -from typing import Callable, Dict, List, Any, Literal, Tuple -from litellm.utils import ModelResponse, Choices, Message, CustomStreamWrapper, Usage -from typing import Optional -from .prompt_templates.factory import prompt_factory, custom_prompt -from litellm.types.completion import ChatCompletionMessageToolCallParam +import copy import enum +import json +import os +import time +import types +from enum import Enum +from typing import Any, Callable, Dict, List, Literal, Optional, Tuple + +import httpx +import requests + +import litellm +from litellm.types.completion import ChatCompletionMessageToolCallParam +from litellm.utils import Choices, CustomStreamWrapper, Message, ModelResponse, Usage + +from .base import BaseLLM +from .prompt_templates.factory import custom_prompt, prompt_factory class HuggingfaceError(Exception): @@ -269,7 +274,7 @@ class Huggingface(BaseLLM): def convert_to_model_response_object( self, completion_response, - model_response, + model_response: litellm.ModelResponse, task: hf_tasks, optional_params, encoding, @@ -278,11 +283,9 @@ class Huggingface(BaseLLM): ): if task == "conversational": if len(completion_response["generated_text"]) > 0: # type: ignore - model_response["choices"][0]["message"][ - "content" - ] = completion_response[ + model_response.choices[0].message.content = completion_response[ # type: ignore "generated_text" - ] # type: ignore + ] elif task == "text-generation-inference": if ( not isinstance(completion_response, list) @@ -295,7 +298,7 @@ class Huggingface(BaseLLM): ) if len(completion_response[0]["generated_text"]) > 0: - model_response["choices"][0]["message"]["content"] = output_parser( + model_response.choices[0].message.content = output_parser( # type: ignore completion_response[0]["generated_text"] ) ## GETTING LOGPROBS + FINISH REASON @@ -310,7 +313,7 @@ class Huggingface(BaseLLM): for token in completion_response[0]["details"]["tokens"]: if token["logprob"] != None: sum_logprob += token["logprob"] - model_response["choices"][0]["message"]._logprob = sum_logprob + setattr(model_response.choices[0].message, "_logprob", sum_logprob) # type: ignore if "best_of" in optional_params and optional_params["best_of"] > 1: if ( "details" in completion_response[0] @@ -337,14 +340,14 @@ class Huggingface(BaseLLM): message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"].extend(choices_list) + model_response.choices.extend(choices_list) elif task == "text-classification": - model_response["choices"][0]["message"]["content"] = json.dumps( + model_response.choices[0].message.content = json.dumps( # type: ignore completion_response ) else: if len(completion_response[0]["generated_text"]) > 0: - model_response["choices"][0]["message"]["content"] = output_parser( + model_response.choices[0].message.content = output_parser( # type: ignore completion_response[0]["generated_text"] ) ## CALCULATING USAGE @@ -371,14 +374,14 @@ class Huggingface(BaseLLM): else: completion_tokens = 0 - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, total_tokens=prompt_tokens + completion_tokens, ) - model_response.usage = usage + setattr(model_response, "usage", usage) model_response._hidden_params["original_response"] = completion_response return model_response @@ -763,10 +766,10 @@ class Huggingface(BaseLLM): self, model: str, input: list, + model_response: litellm.EmbeddingResponse, api_key: Optional[str] = None, api_base: Optional[str] = None, logging_obj=None, - model_response=None, encoding=None, ): super().embedding() @@ -867,15 +870,21 @@ class Huggingface(BaseLLM): ], # flatten list returned from hf } ) - model_response["object"] = "list" - model_response["data"] = output_data - model_response["model"] = model + model_response.object = "list" + model_response.data = output_data + model_response.model = model input_tokens = 0 for text in input: input_tokens += len(encoding.encode(text)) - model_response["usage"] = { - "prompt_tokens": input_tokens, - "total_tokens": input_tokens, - } + setattr( + model_response, + "usage", + litellm.Usage( + **{ + "prompt_tokens": input_tokens, + "total_tokens": input_tokens, + } + ), + ) return model_response diff --git a/litellm/llms/maritalk.py b/litellm/llms/maritalk.py index dfe53e9df0c..c2eb6638261 100644 --- a/litellm/llms/maritalk.py +++ b/litellm/llms/maritalk.py @@ -1,11 +1,15 @@ -import os, types import json +import os +import time +import traceback +import types from enum import Enum +from typing import Callable, List, Optional + import requests # type: ignore -import time, traceback -from typing import Callable, Optional, List -from litellm.utils import ModelResponse, Choices, Message, Usage + import litellm +from litellm.utils import Choices, Message, ModelResponse, Usage class MaritalkError(Exception): @@ -152,9 +156,9 @@ def completion( else: try: if len(completion_response["answer"]) > 0: - model_response["choices"][0]["message"]["content"] = ( - completion_response["answer"] - ) + model_response.choices[0].message.content = completion_response[ # type: ignore + "answer" + ] except Exception as e: raise MaritalkError( message=response.text, status_code=response.status_code @@ -167,8 +171,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/nlp_cloud.py b/litellm/llms/nlp_cloud.py index cd5f17a90b0..84908f26b69 100644 --- a/litellm/llms/nlp_cloud.py +++ b/litellm/llms/nlp_cloud.py @@ -1,9 +1,12 @@ -import os, types import json -from enum import Enum -import requests # type: ignore +import os import time +import types +from enum import Enum from typing import Callable, Optional + +import requests # type: ignore + import litellm from litellm.utils import ModelResponse, Usage @@ -185,7 +188,7 @@ def completion( else: try: if len(completion_response["generated_text"]) > 0: - model_response["choices"][0]["message"]["content"] = ( + model_response.choices[0].message.content = ( # type: ignore completion_response["generated_text"] ) except: @@ -198,8 +201,8 @@ def completion( prompt_tokens = completion_response["nb_input_tokens"] completion_tokens = completion_response["nb_generated_tokens"] - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/ollama.py b/litellm/llms/ollama.py index 1939715b35c..cae47fded49 100644 --- a/litellm/llms/ollama.py +++ b/litellm/llms/ollama.py @@ -1,13 +1,21 @@ -from itertools import chain -import requests, types, time # type: ignore -import json, uuid +import asyncio +import json +import time import traceback -from typing import Optional, List +import types +import uuid +from itertools import chain +from typing import List, Optional + +import aiohttp +import httpx # type: ignore +import requests # type: ignore + import litellm -from litellm.types.utils import ProviderField -import httpx, aiohttp, asyncio # type: ignore -from .prompt_templates.factory import prompt_factory, custom_prompt from litellm import verbose_logger +from litellm.types.utils import ProviderField + +from .prompt_templates.factory import custom_prompt, prompt_factory class OllamaError(Exception): @@ -138,7 +146,6 @@ class OllamaConfig: ) ] - def get_supported_openai_params( self, ): @@ -157,7 +164,8 @@ class OllamaConfig: # ollama wants plain base64 jpeg/png files as images. strip any leading dataURI # and convert to jpeg if necessary. def _convert_image(image): - import base64, io + import base64 + import io try: from PIL import Image @@ -183,13 +191,13 @@ def _convert_image(image): # ollama implementation def get_ollama_response( + model_response: litellm.ModelResponse, api_base="http://localhost:11434", model="llama2", prompt="Why is the sky blue?", optional_params=None, logging_obj=None, acompletion: bool = False, - model_response=None, encoding=None, ): if api_base.endswith("/api/generate"): @@ -271,7 +279,7 @@ def get_ollama_response( response_json = response.json() ## RESPONSE OBJECT - model_response["choices"][0]["finish_reason"] = "stop" + model_response.choices[0].finish_reason = "stop" if data.get("format", "") == "json": function_call = json.loads(response_json["response"]) message = litellm.Message( @@ -287,20 +295,24 @@ def get_ollama_response( } ], ) - model_response["choices"][0]["message"] = message - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].message = message # type: ignore + model_response.choices[0].finish_reason = "tool_calls" else: - model_response["choices"][0]["message"]["content"] = response_json["response"] - model_response["created"] = int(time.time()) - model_response["model"] = "ollama/" + model + model_response.choices[0].message.content = response_json["response"] # type: ignore + model_response.created = int(time.time()) + model_response.model = "ollama/" + model prompt_tokens = response_json.get("prompt_eval_count", len(encoding.encode(prompt, disallowed_special=()))) # type: ignore completion_tokens = response_json.get( "eval_count", len(response_json.get("message", dict()).get("content", "")) ) - model_response["usage"] = litellm.Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + setattr( + model_response, + "usage", + litellm.Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response @@ -346,8 +358,8 @@ def ollama_completion_stream(url, data, logging_obj): ], ) model_response = first_chunk - model_response["choices"][0]["delta"] = delta - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].delta = delta # type: ignore + model_response.choices[0].finish_reason = "tool_calls" yield model_response else: for transformed_chunk in streamwrapper: @@ -401,8 +413,8 @@ async def ollama_async_streaming(url, data, model_response, encoding, logging_ob ], ) model_response = first_chunk - model_response["choices"][0]["delta"] = delta - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].delta = delta # type: ignore + model_response.choices[0].finish_reason = "tool_calls" yield model_response else: async for transformed_chunk in streamwrapper: @@ -418,7 +430,9 @@ async def ollama_async_streaming(url, data, model_response, encoding, logging_ob raise e -async def ollama_acompletion(url, data, model_response, encoding, logging_obj): +async def ollama_acompletion( + url, data, model_response: litellm.ModelResponse, encoding, logging_obj +): data["stream"] = False try: timeout = aiohttp.ClientTimeout(total=litellm.request_timeout) # 10 minutes @@ -442,7 +456,7 @@ async def ollama_acompletion(url, data, model_response, encoding, logging_obj): response_json = await resp.json() ## RESPONSE OBJECT - model_response["choices"][0]["finish_reason"] = "stop" + model_response.choices[0].finish_reason = "stop" if data.get("format", "") == "json": function_call = json.loads(response_json["response"]) message = litellm.Message( @@ -451,30 +465,34 @@ async def ollama_acompletion(url, data, model_response, encoding, logging_obj): { "id": f"call_{str(uuid.uuid4())}", "function": { - "name": function_call.get("name", function_call.get("function", None)), + "name": function_call.get( + "name", function_call.get("function", None) + ), "arguments": json.dumps(function_call["arguments"]), }, "type": "function", } ], ) - model_response["choices"][0]["message"] = message - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].message = message # type: ignore + model_response.choices[0].finish_reason = "tool_calls" else: - model_response["choices"][0]["message"]["content"] = response_json[ - "response" - ] - model_response["created"] = int(time.time()) - model_response["model"] = "ollama/" + data["model"] + model_response.choices[0].message.content = response_json["response"] # type: ignore + model_response.created = int(time.time()) + model_response.model = "ollama/" + data["model"] prompt_tokens = response_json.get("prompt_eval_count", len(encoding.encode(data["prompt"], disallowed_special=()))) # type: ignore completion_tokens = response_json.get( "eval_count", len(response_json.get("message", dict()).get("content", "")), ) - model_response["usage"] = litellm.Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + setattr( + model_response, + "usage", + litellm.Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response except Exception as e: @@ -491,9 +509,9 @@ async def ollama_aembeddings( api_base: str, model: str, prompts: list, + model_response: litellm.EmbeddingResponse, optional_params=None, logging_obj=None, - model_response=None, encoding=None, ): if api_base.endswith("/api/embeddings"): @@ -554,13 +572,19 @@ async def ollama_aembeddings( input_tokens = len(encoding.encode(prompt)) total_input_tokens += input_tokens - model_response["object"] = "list" - model_response["data"] = output_data - model_response["model"] = model - model_response["usage"] = { - "prompt_tokens": total_input_tokens, - "total_tokens": total_input_tokens, - } + model_response.object = "list" + model_response.data = output_data + model_response.model = model + setattr( + model_response, + "usage", + litellm.Usage( + **{ + "prompt_tokens": total_input_tokens, + "total_tokens": total_input_tokens, + } + ), + ) return model_response diff --git a/litellm/llms/ollama_chat.py b/litellm/llms/ollama_chat.py index bb053f5e86d..ebd0f22fb7e 100644 --- a/litellm/llms/ollama_chat.py +++ b/litellm/llms/ollama_chat.py @@ -1,15 +1,17 @@ -from itertools import chain -import requests -import types -import time import json -import uuid +import time import traceback +import types +import uuid +from itertools import chain from typing import Optional -from litellm import verbose_logger -import litellm -import httpx + import aiohttp +import httpx +import requests + +import litellm +from litellm import verbose_logger class OllamaError(Exception): @@ -195,6 +197,7 @@ class OllamaChatConfig: # ollama implementation def get_ollama_response( + model_response: litellm.ModelResponse, api_base="http://localhost:11434", api_key: Optional[str] = None, model="llama2", @@ -202,7 +205,6 @@ def get_ollama_response( optional_params=None, logging_obj=None, acompletion: bool = False, - model_response=None, encoding=None, ): if api_base.endswith("/api/chat"): @@ -295,7 +297,7 @@ def get_ollama_response( response_json = response.json() ## RESPONSE OBJECT - model_response["choices"][0]["finish_reason"] = "stop" + model_response.choices[0].finish_reason = "stop" if data.get("format", "") == "json": function_call = json.loads(response_json["message"]["content"]) message = litellm.Message( @@ -311,22 +313,24 @@ def get_ollama_response( } ], ) - model_response["choices"][0]["message"] = message - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].message = message # type: ignore + model_response.choices[0].finish_reason = "tool_calls" else: - model_response["choices"][0]["message"]["content"] = response_json["message"][ - "content" - ] - model_response["created"] = int(time.time()) - model_response["model"] = "ollama/" + model + model_response.choices[0].message.content = response_json["message"]["content"] # type: ignore + model_response.created = int(time.time()) + model_response.model = "ollama/" + model prompt_tokens = response_json.get("prompt_eval_count", litellm.token_counter(messages=messages)) # type: ignore completion_tokens = response_json.get( "eval_count", litellm.token_counter(text=response_json["message"]["content"]) ) - model_response["usage"] = litellm.Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + setattr( + model_response, + "usage", + litellm.Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response @@ -379,8 +383,8 @@ def ollama_completion_stream(url, api_key, data, logging_obj): ], ) model_response = first_chunk - model_response["choices"][0]["delta"] = delta - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].delta = delta # type: ignore + model_response.choices[0].finish_reason = "tool_calls" yield model_response else: for transformed_chunk in streamwrapper: @@ -434,7 +438,9 @@ async def ollama_async_streaming( { "id": f"call_{str(uuid.uuid4())}", "function": { - "name": function_call.get("name", function_call.get("function", None)), + "name": function_call.get( + "name", function_call.get("function", None) + ), "arguments": json.dumps(function_call["arguments"]), }, "type": "function", @@ -442,8 +448,8 @@ async def ollama_async_streaming( ], ) model_response = first_chunk - model_response["choices"][0]["delta"] = delta - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].delta = delta # type: ignore + model_response.choices[0].finish_reason = "tool_calls" yield model_response else: async for transformed_chunk in streamwrapper: @@ -457,7 +463,7 @@ async def ollama_acompletion( url, api_key: Optional[str], data, - model_response, + model_response: litellm.ModelResponse, encoding, logging_obj, function_name, @@ -492,7 +498,7 @@ async def ollama_acompletion( ) ## RESPONSE OBJECT - model_response["choices"][0]["finish_reason"] = "stop" + model_response.choices[0].finish_reason = "stop" if data.get("format", "") == "json": function_call = json.loads(response_json["message"]["content"]) message = litellm.Message( @@ -510,15 +516,17 @@ async def ollama_acompletion( } ], ) - model_response["choices"][0]["message"] = message - model_response["choices"][0]["finish_reason"] = "tool_calls" + model_response.choices[0].message = message # type: ignore + model_response.choices[0].finish_reason = "tool_calls" else: - model_response["choices"][0]["message"]["content"] = response_json[ + model_response.choices[0].message.content = response_json[ # type: ignore "message" - ]["content"] + ][ + "content" + ] - model_response["created"] = int(time.time()) - model_response["model"] = "ollama_chat/" + data["model"] + model_response.created = int(time.time()) + model_response.model = "ollama_chat/" + data["model"] prompt_tokens = response_json.get("prompt_eval_count", litellm.token_counter(messages=data["messages"])) # type: ignore completion_tokens = response_json.get( "eval_count", @@ -526,10 +534,14 @@ async def ollama_acompletion( text=response_json["message"]["content"], count_response_tokens=True ), ) - model_response["usage"] = litellm.Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + setattr( + model_response, + "usage", + litellm.Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response except Exception as e: diff --git a/litellm/llms/oobabooga.py b/litellm/llms/oobabooga.py index f8f32e0fe49..79d918667cc 100644 --- a/litellm/llms/oobabooga.py +++ b/litellm/llms/oobabooga.py @@ -1,11 +1,14 @@ -import os import json -from enum import Enum -import requests # type: ignore +import os import time +from enum import Enum from typing import Callable, Optional -from litellm.utils import ModelResponse, Usage -from .prompt_templates.factory import prompt_factory, custom_prompt + +import requests # type: ignore + +from litellm.utils import EmbeddingResponse, ModelResponse, Usage + +from .prompt_templates.factory import custom_prompt, prompt_factory class OobaboogaError(Exception): @@ -99,17 +102,15 @@ def completion( ) else: try: - model_response["choices"][0]["message"]["content"] = ( - completion_response["choices"][0]["message"]["content"] - ) + model_response.choices[0].message.content = completion_response["choices"][0]["message"]["content"] # type: ignore except: raise OobaboogaError( message=json.dumps(completion_response), status_code=response.status_code, ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=completion_response["usage"]["prompt_tokens"], completion_tokens=completion_response["usage"]["completion_tokens"], @@ -122,10 +123,10 @@ def completion( def embedding( model: str, input: list, + model_response: EmbeddingResponse, api_key: Optional[str] = None, api_base: Optional[str] = None, logging_obj=None, - model_response=None, optional_params=None, encoding=None, ): @@ -166,7 +167,7 @@ def embedding( ) # Process response data - model_response["data"] = [ + model_response.data = [ { "embedding": completion_response["data"][0]["embedding"], "index": 0, @@ -176,8 +177,12 @@ def embedding( num_tokens = len(completion_response["data"][0]["embedding"]) # Adding metadata to response - model_response.usage = Usage(prompt_tokens=num_tokens, total_tokens=num_tokens) - model_response["object"] = "list" - model_response["model"] = model + setattr( + model_response, + "usage", + Usage(prompt_tokens=num_tokens, total_tokens=num_tokens), + ) + model_response.object = "list" + model_response.model = model return model_response diff --git a/litellm/llms/palm.py b/litellm/llms/palm.py index 4d9953e77ad..b750b800bbc 100644 --- a/litellm/llms/palm.py +++ b/litellm/llms/palm.py @@ -1,12 +1,14 @@ -import types -import traceback import copy import time +import traceback +import types from typing import Callable, Optional -from litellm.utils import ModelResponse, Choices, Message, Usage -import litellm + import httpx + +import litellm from litellm import verbose_logger +from litellm.utils import Choices, Message, ModelResponse, Usage class PalmError(Exception): @@ -164,7 +166,7 @@ def completion( message_obj = Message(content=None) choice_obj = Choices(index=idx + 1, message=message_obj) choices_list.append(choice_obj) - model_response["choices"] = choices_list + model_response.choices = choices_list # type: ignore except Exception as e: verbose_logger.error( "litellm.llms.palm.py::completion(): Exception occured - {}".format(str(e)) @@ -188,8 +190,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = "palm/" + model + model_response.created = int(time.time()) + model_response.model = "palm/" + model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/petals.py b/litellm/llms/petals.py index 334b80d3885..be9f92f071e 100644 --- a/litellm/llms/petals.py +++ b/litellm/llms/petals.py @@ -1,12 +1,16 @@ -import os, types import json -from enum import Enum -import requests # type: ignore +import os import time +import types +from enum import Enum from typing import Callable, Optional + +import requests # type: ignore + import litellm from litellm.utils import ModelResponse, Usage -from .prompt_templates.factory import prompt_factory, custom_prompt + +from .prompt_templates.factory import custom_prompt, prompt_factory class PetalsError(Exception): @@ -151,8 +155,8 @@ def completion( else: try: import torch - from transformers import AutoTokenizer from petals import AutoDistributedModelForCausalLM # type: ignore + from transformers import AutoTokenizer except: raise Exception( "Importing torch, transformers, petals failed\nTry pip installing petals \npip install git+https://github.com/bigscience-workshop/petals" @@ -189,15 +193,15 @@ def completion( output_text = tokenizer.decode(outputs[0]) if len(output_text) > 0: - model_response["choices"][0]["message"]["content"] = output_text + model_response.choices[0].message.content = output_text # type: ignore prompt_tokens = len(encoding.encode(prompt)) completion_tokens = len( encoding.encode(model_response["choices"][0]["message"].get("content")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/predibase.py b/litellm/llms/predibase.py index 534f8e26f27..d028cb1074a 100644 --- a/litellm/llms/predibase.py +++ b/litellm/llms/predibase.py @@ -279,7 +279,7 @@ class PredibaseChatCompletion(BaseLLM): message=f"'generated_text' is not a key response dictionary - {completion_response}", ) if len(completion_response["generated_text"]) > 0: - model_response["choices"][0]["message"]["content"] = self.output_parser( + model_response.choices[0].message.content = self.output_parser( # type: ignore completion_response["generated_text"] ) ## GETTING LOGPROBS + FINISH REASON @@ -294,10 +294,10 @@ class PredibaseChatCompletion(BaseLLM): for token in completion_response["details"]["tokens"]: if token["logprob"] is not None: sum_logprob += token["logprob"] - model_response["choices"][0][ - "message" - ]._logprob = ( - sum_logprob # [TODO] move this to using the actual logprobs + setattr( + model_response.choices[0].message, # type: ignore + "_logprob", + sum_logprob, # [TODO] move this to using the actual logprobs ) if "best_of" in optional_params and optional_params["best_of"] > 1: if ( @@ -325,7 +325,7 @@ class PredibaseChatCompletion(BaseLLM): message=message_obj, ) choices_list.append(choice_obj) - model_response["choices"].extend(choices_list) + model_response.choices.extend(choices_list) ## CALCULATING USAGE prompt_tokens = 0 @@ -351,8 +351,8 @@ class PredibaseChatCompletion(BaseLLM): total_tokens = prompt_tokens + completion_tokens - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/replicate.py b/litellm/llms/replicate.py index 77dc52aae86..0854fda9762 100644 --- a/litellm/llms/replicate.py +++ b/litellm/llms/replicate.py @@ -388,7 +388,7 @@ def process_response( ## Building RESPONSE OBJECT if len(result) > 1: - model_response["choices"][0]["message"]["content"] = result + model_response.choices[0].message.content = result # type :ignore # Calculate usage prompt_tokens = len(encoding.encode(prompt, disallowed_special=())) @@ -398,7 +398,7 @@ def process_response( disallowed_special=(), ) ) - model_response["model"] = "replicate/" + model + model_response.model = "replicate/" + model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -498,7 +498,7 @@ def completion( ## Step1: Start Prediction: gets a prediction url ## Step2: Poll prediction url for response ## Step2: is handled with and without streaming - model_response["created"] = int( + model_response.created = int( time.time() ) # for pricing this must remain right before calling api diff --git a/litellm/llms/sagemaker.py b/litellm/llms/sagemaker.py index 6892445f080..d16d2bd11bf 100644 --- a/litellm/llms/sagemaker.py +++ b/litellm/llms/sagemaker.py @@ -1,16 +1,21 @@ -import os, types, traceback -from enum import Enum -import json -import requests # type: ignore -import time -from typing import Callable, Optional, Any -import litellm -from litellm.utils import ModelResponse, EmbeddingResponse, get_secret, Usage -import sys -from copy import deepcopy -import httpx # type: ignore import io -from .prompt_templates.factory import prompt_factory, custom_prompt +import json +import os +import sys +import time +import traceback +import types +from copy import deepcopy +from enum import Enum +from typing import Any, Callable, Optional + +import httpx # type: ignore +import requests # type: ignore + +import litellm +from litellm.utils import EmbeddingResponse, ModelResponse, Usage, get_secret + +from .prompt_templates.factory import custom_prompt, prompt_factory class SagemakerError(Exception): @@ -377,7 +382,7 @@ def completion( if completion_output.startswith(prompt) and "" in prompt: completion_output = completion_output.replace(prompt, "", 1) - model_response["choices"][0]["message"]["content"] = completion_output + model_response.choices[0].message.content = completion_output # type: ignore except: raise SagemakerError( message=f"LiteLLM Error: Unable to parse sagemaker RAW RESPONSE {json.dumps(completion_response)}", @@ -390,8 +395,8 @@ def completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -597,7 +602,7 @@ async def async_completion( if completion_output.startswith(data["inputs"]) and "" in data["inputs"]: completion_output = completion_output.replace(data["inputs"], "", 1) - model_response["choices"][0]["message"]["content"] = completion_output + model_response.choices[0].message.content = completion_output # type: ignore except: raise SagemakerError( message=f"LiteLLM Error: Unable to parse sagemaker RAW RESPONSE {json.dumps(completion_response)}", @@ -610,8 +615,8 @@ async def async_completion( encoding.encode(model_response["choices"][0]["message"].get("content", "")) ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -741,16 +746,20 @@ def embedding( {"object": "embedding", "index": idx, "embedding": embedding} ) - model_response["object"] = "list" - model_response["data"] = output_data - model_response["model"] = model + model_response.object = "list" + model_response.data = output_data + model_response.model = model input_tokens = 0 for text in input: input_tokens += len(encoding.encode(text)) - model_response["usage"] = Usage( - prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens + setattr( + model_response, + "usage", + Usage( + prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens + ), ) return model_response diff --git a/litellm/llms/together_ai.py b/litellm/llms/together_ai.py index 47453ca8859..3a7e980a326 100644 --- a/litellm/llms/together_ai.py +++ b/litellm/llms/together_ai.py @@ -3,233 +3,233 @@ Deprecated. We now do together ai calls via the openai client. Reference: https://docs.together.ai/docs/openai-api-compatibility """ -import os, types -import json -from enum import Enum -import requests # type: ignore -import time -from typing import Callable, Optional -import litellm -import httpx # type: ignore -from litellm.utils import ModelResponse, Usage -from .prompt_templates.factory import prompt_factory, custom_prompt +# import os, types +# import json +# from enum import Enum +# import requests # type: ignore +# import time +# from typing import Callable, Optional +# import litellm +# import httpx # type: ignore +# from litellm.utils import ModelResponse, Usage +# from .prompt_templates.factory import prompt_factory, custom_prompt -class TogetherAIError(Exception): - def __init__(self, status_code, message): - self.status_code = status_code - self.message = message - self.request = httpx.Request( - method="POST", url="https://api.together.xyz/inference" - ) - self.response = httpx.Response(status_code=status_code, request=self.request) - super().__init__( - self.message - ) # Call the base class constructor with the parameters it needs +# class TogetherAIError(Exception): +# def __init__(self, status_code, message): +# self.status_code = status_code +# self.message = message +# self.request = httpx.Request( +# method="POST", url="https://api.together.xyz/inference" +# ) +# self.response = httpx.Response(status_code=status_code, request=self.request) +# super().__init__( +# self.message +# ) # Call the base class constructor with the parameters it needs -class TogetherAIConfig: - """ - Reference: https://docs.together.ai/reference/inference +# class TogetherAIConfig: +# """ +# Reference: https://docs.together.ai/reference/inference - The class `TogetherAIConfig` provides configuration for the TogetherAI's API interface. Here are the parameters: +# The class `TogetherAIConfig` provides configuration for the TogetherAI's API interface. Here are the parameters: - - `max_tokens` (int32, required): The maximum number of tokens to generate. +# - `max_tokens` (int32, required): The maximum number of tokens to generate. - - `stop` (string, optional): A string sequence that will truncate (stop) the inference text output. For example, "\n\n" will stop generation as soon as the model generates two newlines. +# - `stop` (string, optional): A string sequence that will truncate (stop) the inference text output. For example, "\n\n" will stop generation as soon as the model generates two newlines. - - `temperature` (float, optional): A decimal number that determines the degree of randomness in the response. A value of 1 will always yield the same output. A temperature less than 1 favors more correctness and is appropriate for question answering or summarization. A value greater than 1 introduces more randomness in the output. +# - `temperature` (float, optional): A decimal number that determines the degree of randomness in the response. A value of 1 will always yield the same output. A temperature less than 1 favors more correctness and is appropriate for question answering or summarization. A value greater than 1 introduces more randomness in the output. - - `top_p` (float, optional): The `top_p` (nucleus) parameter is used to dynamically adjust the number of choices for each predicted token based on the cumulative probabilities. It specifies a probability threshold, below which all less likely tokens are filtered out. This technique helps to maintain diversity and generate more fluent and natural-sounding text. +# - `top_p` (float, optional): The `top_p` (nucleus) parameter is used to dynamically adjust the number of choices for each predicted token based on the cumulative probabilities. It specifies a probability threshold, below which all less likely tokens are filtered out. This technique helps to maintain diversity and generate more fluent and natural-sounding text. - - `top_k` (int32, optional): The `top_k` parameter is used to limit the number of choices for the next predicted word or token. It specifies the maximum number of tokens to consider at each step, based on their probability of occurrence. This technique helps to speed up the generation process and can improve the quality of the generated text by focusing on the most likely options. +# - `top_k` (int32, optional): The `top_k` parameter is used to limit the number of choices for the next predicted word or token. It specifies the maximum number of tokens to consider at each step, based on their probability of occurrence. This technique helps to speed up the generation process and can improve the quality of the generated text by focusing on the most likely options. - - `repetition_penalty` (float, optional): A number that controls the diversity of generated text by reducing the likelihood of repeated sequences. Higher values decrease repetition. +# - `repetition_penalty` (float, optional): A number that controls the diversity of generated text by reducing the likelihood of repeated sequences. Higher values decrease repetition. - - `logprobs` (int32, optional): This parameter is not described in the prompt. - """ +# - `logprobs` (int32, optional): This parameter is not described in the prompt. +# """ - max_tokens: Optional[int] = None - stop: Optional[str] = None - temperature: Optional[int] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - repetition_penalty: Optional[float] = None - logprobs: Optional[int] = None +# max_tokens: Optional[int] = None +# stop: Optional[str] = None +# temperature: Optional[int] = None +# top_p: Optional[float] = None +# top_k: Optional[int] = None +# repetition_penalty: Optional[float] = None +# logprobs: Optional[int] = None - def __init__( - self, - max_tokens: Optional[int] = None, - stop: Optional[str] = None, - temperature: Optional[int] = None, - top_p: Optional[float] = None, - top_k: Optional[int] = None, - repetition_penalty: Optional[float] = None, - logprobs: Optional[int] = None, - ) -> None: - locals_ = locals() - for key, value in locals_.items(): - if key != "self" and value is not None: - setattr(self.__class__, key, value) +# def __init__( +# self, +# max_tokens: Optional[int] = None, +# stop: Optional[str] = None, +# temperature: Optional[int] = None, +# top_p: Optional[float] = None, +# top_k: Optional[int] = None, +# repetition_penalty: Optional[float] = None, +# logprobs: Optional[int] = None, +# ) -> None: +# locals_ = locals() +# for key, value in locals_.items(): +# if key != "self" and value is not None: +# setattr(self.__class__, key, value) - @classmethod - def get_config(cls): - return { - k: v - for k, v in cls.__dict__.items() - if not k.startswith("__") - and not isinstance( - v, - ( - types.FunctionType, - types.BuiltinFunctionType, - classmethod, - staticmethod, - ), - ) - and v is not None - } +# @classmethod +# def get_config(cls): +# return { +# k: v +# for k, v in cls.__dict__.items() +# if not k.startswith("__") +# and not isinstance( +# v, +# ( +# types.FunctionType, +# types.BuiltinFunctionType, +# classmethod, +# staticmethod, +# ), +# ) +# and v is not None +# } -def validate_environment(api_key): - if api_key is None: - raise ValueError( - "Missing TogetherAI API Key - A call is being made to together_ai but no key is set either in the environment variables or via params" - ) - headers = { - "accept": "application/json", - "content-type": "application/json", - "Authorization": "Bearer " + api_key, - } - return headers +# def validate_environment(api_key): +# if api_key is None: +# raise ValueError( +# "Missing TogetherAI API Key - A call is being made to together_ai but no key is set either in the environment variables or via params" +# ) +# headers = { +# "accept": "application/json", +# "content-type": "application/json", +# "Authorization": "Bearer " + api_key, +# } +# return headers -def completion( - model: str, - messages: list, - api_base: str, - model_response: ModelResponse, - print_verbose: Callable, - encoding, - api_key, - logging_obj, - custom_prompt_dict={}, - optional_params=None, - litellm_params=None, - logger_fn=None, -): - headers = validate_environment(api_key) +# def completion( +# model: str, +# messages: list, +# api_base: str, +# model_response: ModelResponse, +# print_verbose: Callable, +# encoding, +# api_key, +# logging_obj, +# custom_prompt_dict={}, +# optional_params=None, +# litellm_params=None, +# logger_fn=None, +# ): +# headers = validate_environment(api_key) - ## Load Config - config = litellm.TogetherAIConfig.get_config() - for k, v in config.items(): - if ( - k not in optional_params - ): # completion(top_k=3) > togetherai_config(top_k=3) <- allows for dynamic variables to be passed in - optional_params[k] = v +# ## Load Config +# config = litellm.TogetherAIConfig.get_config() +# for k, v in config.items(): +# if ( +# k not in optional_params +# ): # completion(top_k=3) > togetherai_config(top_k=3) <- allows for dynamic variables to be passed in +# optional_params[k] = v - print_verbose(f"CUSTOM PROMPT DICT: {custom_prompt_dict}; model: {model}") - if model in custom_prompt_dict: - # check if the model has a registered custom prompt - model_prompt_details = custom_prompt_dict[model] - prompt = custom_prompt( - role_dict=model_prompt_details.get("roles", {}), - initial_prompt_value=model_prompt_details.get("initial_prompt_value", ""), - final_prompt_value=model_prompt_details.get("final_prompt_value", ""), - bos_token=model_prompt_details.get("bos_token", ""), - eos_token=model_prompt_details.get("eos_token", ""), - messages=messages, - ) - else: - prompt = prompt_factory( - model=model, - messages=messages, - api_key=api_key, - custom_llm_provider="together_ai", - ) # api key required to query together ai model list +# print_verbose(f"CUSTOM PROMPT DICT: {custom_prompt_dict}; model: {model}") +# if model in custom_prompt_dict: +# # check if the model has a registered custom prompt +# model_prompt_details = custom_prompt_dict[model] +# prompt = custom_prompt( +# role_dict=model_prompt_details.get("roles", {}), +# initial_prompt_value=model_prompt_details.get("initial_prompt_value", ""), +# final_prompt_value=model_prompt_details.get("final_prompt_value", ""), +# bos_token=model_prompt_details.get("bos_token", ""), +# eos_token=model_prompt_details.get("eos_token", ""), +# messages=messages, +# ) +# else: +# prompt = prompt_factory( +# model=model, +# messages=messages, +# api_key=api_key, +# custom_llm_provider="together_ai", +# ) # api key required to query together ai model list - data = { - "model": model, - "prompt": prompt, - "request_type": "language-model-inference", - **optional_params, - } +# data = { +# "model": model, +# "prompt": prompt, +# "request_type": "language-model-inference", +# **optional_params, +# } - ## LOGGING - logging_obj.pre_call( - input=prompt, - api_key=api_key, - additional_args={ - "complete_input_dict": data, - "headers": headers, - "api_base": api_base, - }, - ) - ## COMPLETION CALL - if "stream_tokens" in optional_params and optional_params["stream_tokens"] == True: - response = requests.post( - api_base, - headers=headers, - data=json.dumps(data), - stream=optional_params["stream_tokens"], - ) - return response.iter_lines() - else: - response = requests.post(api_base, headers=headers, data=json.dumps(data)) - ## LOGGING - logging_obj.post_call( - input=prompt, - api_key=api_key, - original_response=response.text, - additional_args={"complete_input_dict": data}, - ) - print_verbose(f"raw model_response: {response.text}") - ## RESPONSE OBJECT - if response.status_code != 200: - raise TogetherAIError( - status_code=response.status_code, message=response.text - ) - completion_response = response.json() +# ## LOGGING +# logging_obj.pre_call( +# input=prompt, +# api_key=api_key, +# additional_args={ +# "complete_input_dict": data, +# "headers": headers, +# "api_base": api_base, +# }, +# ) +# ## COMPLETION CALL +# if "stream_tokens" in optional_params and optional_params["stream_tokens"] == True: +# response = requests.post( +# api_base, +# headers=headers, +# data=json.dumps(data), +# stream=optional_params["stream_tokens"], +# ) +# return response.iter_lines() +# else: +# response = requests.post(api_base, headers=headers, data=json.dumps(data)) +# ## LOGGING +# logging_obj.post_call( +# input=prompt, +# api_key=api_key, +# original_response=response.text, +# additional_args={"complete_input_dict": data}, +# ) +# print_verbose(f"raw model_response: {response.text}") +# ## RESPONSE OBJECT +# if response.status_code != 200: +# raise TogetherAIError( +# status_code=response.status_code, message=response.text +# ) +# completion_response = response.json() - if "error" in completion_response: - raise TogetherAIError( - message=json.dumps(completion_response), - status_code=response.status_code, - ) - elif "error" in completion_response["output"]: - raise TogetherAIError( - message=json.dumps(completion_response["output"]), - status_code=response.status_code, - ) +# if "error" in completion_response: +# raise TogetherAIError( +# message=json.dumps(completion_response), +# status_code=response.status_code, +# ) +# elif "error" in completion_response["output"]: +# raise TogetherAIError( +# message=json.dumps(completion_response["output"]), +# status_code=response.status_code, +# ) - if len(completion_response["output"]["choices"][0]["text"]) >= 0: - model_response["choices"][0]["message"]["content"] = completion_response[ - "output" - ]["choices"][0]["text"] +# if len(completion_response["output"]["choices"][0]["text"]) >= 0: +# model_response.choices[0].message.content = completion_response["output"][ +# "choices" +# ][0]["text"] - ## CALCULATING USAGE - print_verbose( - f"CALCULATING TOGETHERAI TOKEN USAGE. Model Response: {model_response}; model_response['choices'][0]['message'].get('content', ''): {model_response['choices'][0]['message'].get('content', None)}" - ) - prompt_tokens = len(encoding.encode(prompt)) - completion_tokens = len( - encoding.encode(model_response["choices"][0]["message"].get("content", "")) - ) - if "finish_reason" in completion_response["output"]["choices"][0]: - model_response.choices[0].finish_reason = completion_response["output"][ - "choices" - ][0]["finish_reason"] - model_response["created"] = int(time.time()) - model_response["model"] = "together_ai/" + model - usage = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, - ) - setattr(model_response, "usage", usage) - return model_response +# ## CALCULATING USAGE +# print_verbose( +# f"CALCULATING TOGETHERAI TOKEN USAGE. Model Response: {model_response}; model_response['choices'][0]['message'].get('content', ''): {model_response['choices'][0]['message'].get('content', None)}" +# ) +# prompt_tokens = len(encoding.encode(prompt)) +# completion_tokens = len( +# encoding.encode(model_response["choices"][0]["message"].get("content", "")) +# ) +# if "finish_reason" in completion_response["output"]["choices"][0]: +# model_response.choices[0].finish_reason = completion_response["output"][ +# "choices" +# ][0]["finish_reason"] +# model_response["created"] = int(time.time()) +# model_response["model"] = "together_ai/" + model +# usage = Usage( +# prompt_tokens=prompt_tokens, +# completion_tokens=completion_tokens, +# total_tokens=prompt_tokens + completion_tokens, +# ) +# setattr(model_response, "usage", usage) +# return model_response -def embedding(): - # logic for parsing in - calling - parsing out model embedding calls - pass +# def embedding(): +# # logic for parsing in - calling - parsing out model embedding calls +# pass diff --git a/litellm/llms/vertex_ai.py b/litellm/llms/vertex_ai.py index 31fd2320213..c891a86eeb8 100644 --- a/litellm/llms/vertex_ai.py +++ b/litellm/llms/vertex_ai.py @@ -852,16 +852,14 @@ def completion( ## RESPONSE OBJECT if isinstance(completion_response, litellm.Message): - model_response["choices"][0]["message"] = completion_response + model_response.choices[0].message = completion_response # type: ignore elif len(str(completion_response)) > 0: - model_response["choices"][0]["message"]["content"] = str( - completion_response - ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.choices[0].message.content = str(completion_response) # type: ignore + model_response.created = int(time.time()) + model_response.model = model ## CALCULATING USAGE if model in litellm.vertex_language_models and response_obj is not None: - model_response["choices"][0].finish_reason = map_finish_reason( + model_response.choices[0].finish_reason = map_finish_reason( response_obj.candidates[0].finish_reason.name ) usage = Usage( @@ -912,7 +910,7 @@ async def async_completion( request_str: str, print_verbose: Callable, logging_obj, - encoding=None, + encoding, client_options=None, instances=None, vertex_project=None, @@ -1088,16 +1086,16 @@ async def async_completion( ## RESPONSE OBJECT if isinstance(completion_response, litellm.Message): - model_response["choices"][0]["message"] = completion_response + model_response.choices[0].message = completion_response # type: ignore elif len(str(completion_response)) > 0: - model_response["choices"][0]["message"]["content"] = str( + model_response.choices[0].message.content = str( # type: ignore completion_response ) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model ## CALCULATING USAGE if model in litellm.vertex_language_models and response_obj is not None: - model_response["choices"][0].finish_reason = map_finish_reason( + model_response.choices[0].finish_reason = map_finish_reason( response_obj.candidates[0].finish_reason.name ) usage = Usage( @@ -1377,16 +1375,16 @@ class VertexAITextEmbeddingConfig(BaseModel): def embedding( model: str, input: Union[list, str], + print_verbose, + model_response: litellm.EmbeddingResponse, + optional_params: dict, api_key: Optional[str] = None, logging_obj=None, - model_response=None, - optional_params=None, encoding=None, vertex_project=None, vertex_location=None, vertex_credentials=None, aembedding=False, - print_verbose=None, ): # logic for parsing in - calling - parsing out model embedding calls try: @@ -1484,15 +1482,15 @@ def embedding( "embedding": embedding.values, } ) - input_tokens += embedding.statistics.token_count - model_response["object"] = "list" - model_response["data"] = embedding_response - model_response["model"] = model + input_tokens += embedding.statistics.token_count # type: ignore + model_response.object = "list" + model_response.data = embedding_response + model_response.model = model usage = Usage( prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens ) - model_response.usage = usage + setattr(model_response, "usage", usage) return model_response @@ -1500,8 +1498,8 @@ def embedding( async def async_embedding( model: str, input: Union[list, str], + model_response: litellm.EmbeddingResponse, logging_obj=None, - model_response=None, optional_params=None, encoding=None, client=None, @@ -1541,11 +1539,11 @@ async def async_embedding( ) input_tokens += embedding.statistics.token_count - model_response["object"] = "list" - model_response["data"] = embedding_response - model_response["model"] = model + model_response.object = "list" + model_response.data = embedding_response + model_response.model = model usage = Usage( prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens ) - model_response.usage = usage + setattr(model_response, "usage", usage) return model_response diff --git a/litellm/llms/vertex_ai_anthropic.py b/litellm/llms/vertex_ai_anthropic.py index 44a7a448eb1..b8362d5a5c9 100644 --- a/litellm/llms/vertex_ai_anthropic.py +++ b/litellm/llms/vertex_ai_anthropic.py @@ -367,8 +367,8 @@ async def async_completion( prompt_tokens = message.usage.input_tokens completion_tokens = message.usage.output_tokens - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/vllm.py b/litellm/llms/vllm.py index b2a9dd54db7..f261b7297c2 100644 --- a/litellm/llms/vllm.py +++ b/litellm/llms/vllm.py @@ -1,11 +1,15 @@ -import os import json +import os +import time # type: ignore from enum import Enum +from typing import Any, Callable + +import httpx import requests # type: ignore -import time, httpx # type: ignore -from typing import Callable, Any + from litellm.utils import ModelResponse, Usage -from .prompt_templates.factory import prompt_factory, custom_prompt + +from .prompt_templates.factory import custom_prompt, prompt_factory llm = None @@ -91,14 +95,14 @@ def completion( ) print_verbose(f"raw model_response: {outputs}") ## RESPONSE OBJECT - model_response["choices"][0]["message"]["content"] = outputs[0].outputs[0].text + model_response.choices[0].message.content = outputs[0].outputs[0].text # type: ignore ## CALCULATING USAGE prompt_tokens = len(outputs[0].prompt_token_ids) completion_tokens = len(outputs[0].outputs[0].token_ids) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, @@ -173,14 +177,14 @@ def batch_completions( for output in outputs: model_response = ModelResponse() ## RESPONSE OBJECT - model_response["choices"][0]["message"]["content"] = output.outputs[0].text + model_response.choices[0].message.content = output.outputs[0].text # type: ignore ## CALCULATING USAGE prompt_tokens = len(output.prompt_token_ids) completion_tokens = len(output.outputs[0].token_ids) - model_response["created"] = int(time.time()) - model_response["model"] = model + model_response.created = int(time.time()) + model_response.model = model usage = Usage( prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index a3d31555748..c01efd8adcb 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -591,9 +591,9 @@ class IBMWatsonXAI(BaseLLM): self, model: str, input: Union[list, str], + model_response: litellm.EmbeddingResponse, api_key: Optional[str] = None, logging_obj=None, - model_response=None, optional_params=None, encoding=None, print_verbose=None, @@ -610,7 +610,7 @@ class IBMWatsonXAI(BaseLLM): if k not in optional_params: optional_params[k] = v - model_response["model"] = model + model_response.model = model # Load auth variables from environment variables if isinstance(input, str): From fe27182e8e218ff5a7e2f70c936f27f3316f00ef Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Thu, 11 Jul 2024 13:26:59 -0700 Subject: [PATCH 74/89] Add missing space in "Failed Tracking Cost" msg Fixes stuff like this: ``` Message: Failed Tracking Cost forerror in tracking cost callback - Traceback (most recent call last): ^^^^^^^^ ``` --- litellm/integrations/slack_alerting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/integrations/slack_alerting.py b/litellm/integrations/slack_alerting.py index b7b62b61f62..8ae23c629f9 100644 --- a/litellm/integrations/slack_alerting.py +++ b/litellm/integrations/slack_alerting.py @@ -675,7 +675,7 @@ class SlackAlerting(CustomLogger): async def failed_tracking_alert(self, error_message: str): """Raise alert when tracking failed for specific model""" _cache: DualCache = self.internal_usage_cache - message = "Failed Tracking Cost for" + error_message + message = "Failed Tracking Cost for " + error_message _cache_key = "budget_alerts:failed_tracking:{}".format(message) result = await _cache.async_get_cache(key=_cache_key) if result is None: From 389a51e05d912ba797a9147d57c9caaf35ac84a3 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 13:36:55 -0700 Subject: [PATCH 75/89] fix: fix linting errors --- .pre-commit-config.yaml | 12 +-- litellm/__init__.py | 1 + litellm/llms/together_ai.py | 146 ++++++++++++++++++------------------ litellm/main.py | 72 ++++-------------- 4 files changed, 97 insertions(+), 134 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d429bc6b8c1..a33473b7246 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: local hooks: - # - id: mypy - # name: mypy - # entry: python3 -m mypy --ignore-missing-imports - # language: system - # types: [python] - # files: ^litellm/ + - id: mypy + name: mypy + entry: python3 -m mypy --ignore-missing-imports + language: system + types: [python] + files: ^litellm/ - id: isort name: isort entry: isort diff --git a/litellm/__init__.py b/litellm/__init__.py index 6393664c66b..1944f886b34 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -789,6 +789,7 @@ from .utils import ( get_api_base, get_first_chars_messages, ModelResponse, + EmbeddingResponse, ImageResponse, get_provider_fields, ) diff --git a/litellm/llms/together_ai.py b/litellm/llms/together_ai.py index 3a7e980a326..3adbcae37e2 100644 --- a/litellm/llms/together_ai.py +++ b/litellm/llms/together_ai.py @@ -3,92 +3,96 @@ Deprecated. We now do together ai calls via the openai client. Reference: https://docs.together.ai/docs/openai-api-compatibility """ -# import os, types -# import json -# from enum import Enum -# import requests # type: ignore -# import time -# from typing import Callable, Optional -# import litellm -# import httpx # type: ignore -# from litellm.utils import ModelResponse, Usage -# from .prompt_templates.factory import prompt_factory, custom_prompt +import json +import os +import time +import types +from enum import Enum +from typing import Callable, Optional + +import httpx # type: ignore +import requests # type: ignore + +import litellm +from litellm.utils import ModelResponse, Usage + +from .prompt_templates.factory import custom_prompt, prompt_factory -# class TogetherAIError(Exception): -# def __init__(self, status_code, message): -# self.status_code = status_code -# self.message = message -# self.request = httpx.Request( -# method="POST", url="https://api.together.xyz/inference" -# ) -# self.response = httpx.Response(status_code=status_code, request=self.request) -# super().__init__( -# self.message -# ) # Call the base class constructor with the parameters it needs +class TogetherAIError(Exception): + def __init__(self, status_code, message): + self.status_code = status_code + self.message = message + self.request = httpx.Request( + method="POST", url="https://api.together.xyz/inference" + ) + self.response = httpx.Response(status_code=status_code, request=self.request) + super().__init__( + self.message + ) # Call the base class constructor with the parameters it needs -# class TogetherAIConfig: -# """ -# Reference: https://docs.together.ai/reference/inference +class TogetherAIConfig: + """ + Reference: https://docs.together.ai/reference/inference -# The class `TogetherAIConfig` provides configuration for the TogetherAI's API interface. Here are the parameters: + The class `TogetherAIConfig` provides configuration for the TogetherAI's API interface. Here are the parameters: -# - `max_tokens` (int32, required): The maximum number of tokens to generate. + - `max_tokens` (int32, required): The maximum number of tokens to generate. -# - `stop` (string, optional): A string sequence that will truncate (stop) the inference text output. For example, "\n\n" will stop generation as soon as the model generates two newlines. + - `stop` (string, optional): A string sequence that will truncate (stop) the inference text output. For example, "\n\n" will stop generation as soon as the model generates two newlines. -# - `temperature` (float, optional): A decimal number that determines the degree of randomness in the response. A value of 1 will always yield the same output. A temperature less than 1 favors more correctness and is appropriate for question answering or summarization. A value greater than 1 introduces more randomness in the output. + - `temperature` (float, optional): A decimal number that determines the degree of randomness in the response. A value of 1 will always yield the same output. A temperature less than 1 favors more correctness and is appropriate for question answering or summarization. A value greater than 1 introduces more randomness in the output. -# - `top_p` (float, optional): The `top_p` (nucleus) parameter is used to dynamically adjust the number of choices for each predicted token based on the cumulative probabilities. It specifies a probability threshold, below which all less likely tokens are filtered out. This technique helps to maintain diversity and generate more fluent and natural-sounding text. + - `top_p` (float, optional): The `top_p` (nucleus) parameter is used to dynamically adjust the number of choices for each predicted token based on the cumulative probabilities. It specifies a probability threshold, below which all less likely tokens are filtered out. This technique helps to maintain diversity and generate more fluent and natural-sounding text. -# - `top_k` (int32, optional): The `top_k` parameter is used to limit the number of choices for the next predicted word or token. It specifies the maximum number of tokens to consider at each step, based on their probability of occurrence. This technique helps to speed up the generation process and can improve the quality of the generated text by focusing on the most likely options. + - `top_k` (int32, optional): The `top_k` parameter is used to limit the number of choices for the next predicted word or token. It specifies the maximum number of tokens to consider at each step, based on their probability of occurrence. This technique helps to speed up the generation process and can improve the quality of the generated text by focusing on the most likely options. -# - `repetition_penalty` (float, optional): A number that controls the diversity of generated text by reducing the likelihood of repeated sequences. Higher values decrease repetition. + - `repetition_penalty` (float, optional): A number that controls the diversity of generated text by reducing the likelihood of repeated sequences. Higher values decrease repetition. -# - `logprobs` (int32, optional): This parameter is not described in the prompt. -# """ + - `logprobs` (int32, optional): This parameter is not described in the prompt. + """ -# max_tokens: Optional[int] = None -# stop: Optional[str] = None -# temperature: Optional[int] = None -# top_p: Optional[float] = None -# top_k: Optional[int] = None -# repetition_penalty: Optional[float] = None -# logprobs: Optional[int] = None + max_tokens: Optional[int] = None + stop: Optional[str] = None + temperature: Optional[int] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + repetition_penalty: Optional[float] = None + logprobs: Optional[int] = None -# def __init__( -# self, -# max_tokens: Optional[int] = None, -# stop: Optional[str] = None, -# temperature: Optional[int] = None, -# top_p: Optional[float] = None, -# top_k: Optional[int] = None, -# repetition_penalty: Optional[float] = None, -# logprobs: Optional[int] = None, -# ) -> None: -# locals_ = locals() -# for key, value in locals_.items(): -# if key != "self" and value is not None: -# setattr(self.__class__, key, value) + def __init__( + self, + max_tokens: Optional[int] = None, + stop: Optional[str] = None, + temperature: Optional[int] = None, + top_p: Optional[float] = None, + top_k: Optional[int] = None, + repetition_penalty: Optional[float] = None, + logprobs: Optional[int] = None, + ) -> None: + locals_ = locals() + for key, value in locals_.items(): + if key != "self" and value is not None: + setattr(self.__class__, key, value) -# @classmethod -# def get_config(cls): -# return { -# k: v -# for k, v in cls.__dict__.items() -# if not k.startswith("__") -# and not isinstance( -# v, -# ( -# types.FunctionType, -# types.BuiltinFunctionType, -# classmethod, -# staticmethod, -# ), -# ) -# and v is not None -# } + @classmethod + def get_config(cls): + return { + k: v + for k, v in cls.__dict__.items() + if not k.startswith("__") + and not isinstance( + v, + ( + types.FunctionType, + types.BuiltinFunctionType, + classmethod, + staticmethod, + ), + ) + and v is not None + } # def validate_environment(api_key): diff --git a/litellm/main.py b/litellm/main.py index bc40194b435..0aeff318807 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1934,51 +1934,7 @@ def completion( """ Deprecated. We now do together ai calls via the openai client - https://docs.together.ai/docs/openai-api-compatibility """ - custom_llm_provider = "together_ai" - together_ai_key = ( - api_key - or litellm.togetherai_api_key - or get_secret("TOGETHER_AI_TOKEN") - or get_secret("TOGETHERAI_API_KEY") - or litellm.api_key - ) - - api_base = ( - api_base - or litellm.api_base - or get_secret("TOGETHERAI_API_BASE") - or "https://api.together.xyz/inference" - ) - - custom_prompt_dict = custom_prompt_dict or litellm.custom_prompt_dict - - model_response = together_ai.completion( - model=model, - messages=messages, - api_base=api_base, - model_response=model_response, - print_verbose=print_verbose, - optional_params=optional_params, - litellm_params=litellm_params, - logger_fn=logger_fn, - encoding=encoding, - api_key=together_ai_key, - logging_obj=logging, - custom_prompt_dict=custom_prompt_dict, - ) - if ( - "stream_tokens" in optional_params - and optional_params["stream_tokens"] == True - ): - # don't try to access stream object, - response = CustomStreamWrapper( - model_response, - model, - custom_llm_provider="together_ai", - logging_obj=logging, - ) - return response - response = model_response + pass elif custom_llm_provider == "palm": palm_api_key = api_key or get_secret("PALM_API_KEY") or litellm.api_key @@ -2461,10 +2417,10 @@ def completion( ## LOGGING generator = ollama.get_ollama_response( - api_base, - model, - prompt, - optional_params, + api_base=api_base, + model=model, + prompt=prompt, + optional_params=optional_params, logging_obj=logging, acompletion=acompletion, model_response=model_response, @@ -2490,11 +2446,11 @@ def completion( ) ## LOGGING generator = ollama_chat.get_ollama_response( - api_base, - api_key, - model, - messages, - optional_params, + api_base=api_base, + api_key=api_key, + model=model, + messages=messages, + optional_params=optional_params, logging_obj=logging, acompletion=acompletion, model_response=model_response, @@ -3465,7 +3421,7 @@ def embedding( or api_base or get_secret("OLLAMA_API_BASE") or "http://localhost:11434" - ) + ) # type: ignore if isinstance(input, str): input = [input] if not all(isinstance(item, str) for item in input): @@ -3475,9 +3431,11 @@ def embedding( llm_provider="ollama", # type: ignore ) ollama_embeddings_fn = ( - ollama.ollama_aembeddings if aembedding else ollama.ollama_embeddings + ollama.ollama_aembeddings + if aembedding is True + else ollama.ollama_embeddings ) - response = ollama_embeddings_fn( + response = ollama_embeddings_fn( # type: ignore api_base=api_base, model=model, prompts=input, From 77068575d7fecba901d04ce1c01aa3f73acc8dd8 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 13:44:29 -0700 Subject: [PATCH 76/89] fix: fix linting errors --- litellm/llms/clarifai.py | 2 +- litellm/llms/replicate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/llms/clarifai.py b/litellm/llms/clarifai.py index fb517d98dee..613ee5ced19 100644 --- a/litellm/llms/clarifai.py +++ b/litellm/llms/clarifai.py @@ -128,7 +128,7 @@ def process_response( message=message_obj, ) choices_list.append(choice_obj) - model_response.choices = choices_list + model_response.choices = choices_list # type: ignore except Exception as e: raise ClarifaiError( diff --git a/litellm/llms/replicate.py b/litellm/llms/replicate.py index 0854fda9762..1dd29fd7d69 100644 --- a/litellm/llms/replicate.py +++ b/litellm/llms/replicate.py @@ -388,7 +388,7 @@ def process_response( ## Building RESPONSE OBJECT if len(result) > 1: - model_response.choices[0].message.content = result # type :ignore + model_response.choices[0].message.content = result # type: ignore # Calculate usage prompt_tokens = len(encoding.encode(prompt, disallowed_special=())) From 52b293e831e6fcdf6bb088198494098675606385 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 11 Jul 2024 14:11:21 -0700 Subject: [PATCH 77/89] fix(types/utils.py): message role is always 'assistant' --- litellm/types/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 2e0ae425c71..3ae9fb05662 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -305,7 +305,7 @@ class Message(OpenAIObject): ): init_values = { "content": content, - "role": role, + "role": "assistant", "function_call": ( FunctionCall(**function_call) if function_call is not None else None ), From aa9493f1778e1e25caa3a23b6d4edfd571777a6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 01:28:16 +0000 Subject: [PATCH 78/89] Bump braces from 3.0.2 to 3.0.3 in /ui Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] --- ui/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index acf436e46af..8a1ddf5057a 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -416,12 +416,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "peer": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -754,9 +754,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "peer": true, "dependencies": { "to-regex-range": "^5.0.1" From 9fe5d9caa6bad63a247b7efe36195b4b2ea531cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 01:28:58 +0000 Subject: [PATCH 79/89] Bump azure-identity from 1.16.0 to 1.16.1 Bumps [azure-identity](https://github.com/Azure/azure-sdk-for-python) from 1.16.0 to 1.16.1. - [Release notes](https://github.com/Azure/azure-sdk-for-python/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/esrp_release.md) - [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-identity_1.16.0...azure-identity_1.16.1) --- updated-dependencies: - dependency-name: azure-identity dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 286333b30e6..d1b428ac7c0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -225,13 +225,13 @@ aio = ["aiohttp (>=3.0)"] [[package]] name = "azure-identity" -version = "1.16.0" +version = "1.16.1" description = "Microsoft Azure Identity Library for Python" optional = true python-versions = ">=3.8" files = [ - {file = "azure-identity-1.16.0.tar.gz", hash = "sha256:6ff1d667cdcd81da1ceab42f80a0be63ca846629f518a922f7317a7e3c844e1b"}, - {file = "azure_identity-1.16.0-py3-none-any.whl", hash = "sha256:722fdb60b8fdd55fa44dc378b8072f4b419b56a5e54c0de391f644949f3a826f"}, + {file = "azure-identity-1.16.1.tar.gz", hash = "sha256:6d93f04468f240d59246d8afde3091494a5040d4f141cad0f49fc0c399d0d91e"}, + {file = "azure_identity-1.16.1-py3-none-any.whl", hash = "sha256:8fb07c25642cd4ac422559a8b50d3e77f73dcc2bbfaba419d06d6c9d7cff6726"}, ] [package.dependencies] From d04e2dc2f15e0a35121aa1d178cb18a76e9c66ab Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 18:44:21 -0700 Subject: [PATCH 80/89] ui fix allow setting custom model name for OpenAI compatible --- ui/litellm-dashboard/src/components/model_dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 2e62c053a5f..90cad59f095 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1717,7 +1717,7 @@ const ModelDashboard: React.FC = ({ tooltip="Actual model name used for making litellm.completion() call." className="mb-0" > - {selectedProvider === Providers.Azure ? ( + { (selectedProvider === Providers.Azure) || (selectedProvider === Providers.OpenAI_Compatible) || (selectedProvider === Providers.Ollama) ? ( ) : providerModels.length > 0 ? ( From dc256699f5f75163c3e1a476e50d07331627da52 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 18:45:17 -0700 Subject: [PATCH 81/89] ui fix allow setting custom model name for OpenAI compatible --- ui/litellm-dashboard/src/components/model_dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 2e62c053a5f..90cad59f095 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1717,7 +1717,7 @@ const ModelDashboard: React.FC = ({ tooltip="Actual model name used for making litellm.completion() call." className="mb-0" > - {selectedProvider === Providers.Azure ? ( + { (selectedProvider === Providers.Azure) || (selectedProvider === Providers.OpenAI_Compatible) || (selectedProvider === Providers.Ollama) ? ( ) : providerModels.length > 0 ? ( From 26fc873344b1c97a5dd56beabc183f36faa41bec Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 19:08:54 -0700 Subject: [PATCH 82/89] fix setting router settings on ui --- ui/litellm-dashboard/src/components/general_settings.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/litellm-dashboard/src/components/general_settings.tsx b/ui/litellm-dashboard/src/components/general_settings.tsx index 62e37f1755f..183236e223a 100644 --- a/ui/litellm-dashboard/src/components/general_settings.tsx +++ b/ui/litellm-dashboard/src/components/general_settings.tsx @@ -250,6 +250,10 @@ const GeneralSettings: React.FC = ({ getCallbacksCall(accessToken, userID, userRole).then((data) => { console.log("callbacks", data); let router_settings = data.router_settings; + // remove "model_group_retry_policy" from general_settings if exists + if ("model_group_retry_policy" in router_settings) { + delete router_settings["model_group_retry_policy"]; + } setRouterSettings(router_settings); }); getGeneralSettingsCall(accessToken).then((data) => { From aec468c0e913579d09f339a0a00ba1a3dedb6fad Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 11 Jul 2024 19:13:08 -0700 Subject: [PATCH 83/89] ui new build --- litellm/proxy/_experimental/out/404.html | 1 + .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/131-19b05e5ce40fa85d.js | 8 ++++++++ .../out/_next/static/chunks/131-6a03368053f9d26d.js | 8 -------- .../{759-83a8bdddfe32b5d9.js => 759-d7572f2a46f911d5.js} | 4 ++-- .../out/_next/static/chunks/app/page-0cfbdaa2bf8fb022.js | 1 - .../out/_next/static/chunks/app/page-1cc1412fb406fc70.js | 1 + litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- litellm/proxy/_experimental/out/model_hub.html | 1 + litellm/proxy/_experimental/out/model_hub.txt | 4 ++-- litellm/proxy/_experimental/out/onboarding.html | 1 + litellm/proxy/_experimental/out/onboarding.txt | 2 +- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/131-19b05e5ce40fa85d.js | 8 ++++++++ .../out/_next/static/chunks/131-6a03368053f9d26d.js | 8 -------- .../{759-83a8bdddfe32b5d9.js => 759-d7572f2a46f911d5.js} | 4 ++-- .../out/_next/static/chunks/app/page-0cfbdaa2bf8fb022.js | 1 - .../out/_next/static/chunks/app/page-1cc1412fb406fc70.js | 1 + ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- ui/litellm-dashboard/out/model_hub.html | 2 +- ui/litellm-dashboard/out/model_hub.txt | 4 ++-- ui/litellm-dashboard/out/onboarding.html | 2 +- ui/litellm-dashboard/out/onboarding.txt | 2 +- 28 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 litellm/proxy/_experimental/out/404.html rename litellm/proxy/_experimental/out/_next/static/{LmgW0mreu0hjU2N9CAPDM => aCz2wdplG6aqWrQnod4_6}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{LmgW0mreu0hjU2N9CAPDM => aCz2wdplG6aqWrQnod4_6}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/131-19b05e5ce40fa85d.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/131-6a03368053f9d26d.js rename litellm/proxy/_experimental/out/_next/static/chunks/{759-83a8bdddfe32b5d9.js => 759-d7572f2a46f911d5.js} (53%) delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-0cfbdaa2bf8fb022.js create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-1cc1412fb406fc70.js create mode 100644 litellm/proxy/_experimental/out/model_hub.html create mode 100644 litellm/proxy/_experimental/out/onboarding.html rename ui/litellm-dashboard/out/_next/static/{LmgW0mreu0hjU2N9CAPDM => aCz2wdplG6aqWrQnod4_6}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{LmgW0mreu0hjU2N9CAPDM => aCz2wdplG6aqWrQnod4_6}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/131-19b05e5ce40fa85d.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/131-6a03368053f9d26d.js rename ui/litellm-dashboard/out/_next/static/chunks/{759-83a8bdddfe32b5d9.js => 759-d7572f2a46f911d5.js} (53%) delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-0cfbdaa2bf8fb022.js create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-1cc1412fb406fc70.js diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html new file mode 100644 index 00000000000..fcdc3749f36 --- /dev/null +++ b/litellm/proxy/_experimental/out/404.html @@ -0,0 +1 @@ +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/LmgW0mreu0hjU2N9CAPDM/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/aCz2wdplG6aqWrQnod4_6/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/LmgW0mreu0hjU2N9CAPDM/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/aCz2wdplG6aqWrQnod4_6/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/LmgW0mreu0hjU2N9CAPDM/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/aCz2wdplG6aqWrQnod4_6/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/LmgW0mreu0hjU2N9CAPDM/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/aCz2wdplG6aqWrQnod4_6/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/131-19b05e5ce40fa85d.js b/litellm/proxy/_experimental/out/_next/static/chunks/131-19b05e5ce40fa85d.js new file mode 100644 index 00000000000..92aaed0ddd9 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/131-19b05e5ce40fa85d.js @@ -0,0 +1,8 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[131],{84174:function(e,t,n){n.d(t,{Z:function(){return s}});var a=n(14749),r=n(64090),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"},o=n(60688),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},50459:function(e,t,n){n.d(t,{Z:function(){return s}});var a=n(14749),r=n(64090),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"}}]},name:"right",theme:"outlined"},o=n(60688),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},92836:function(e,t,n){n.d(t,{Z:function(){return p}});var a=n(69703),r=n(80991),i=n(2898),o=n(99250),s=n(65492),l=n(64090),c=n(41608),d=n(50027);n(18174),n(21871),n(41213);let u=(0,s.fn)("Tab"),p=l.forwardRef((e,t)=>{let{icon:n,className:p,children:g}=e,m=(0,a._T)(e,["icon","className","children"]),b=(0,l.useContext)(c.O),f=(0,l.useContext)(d.Z);return l.createElement(r.O,Object.assign({ref:t,className:(0,o.q)(u("root"),"flex whitespace-nowrap truncate max-w-xs outline-none focus:ring-0 text-tremor-default transition duration-100",f?(0,s.bM)(f,i.K.text).selectTextColor:"solid"===b?"ui-selected:text-tremor-content-emphasis dark:ui-selected:text-dark-tremor-content-emphasis":"ui-selected:text-tremor-brand dark:ui-selected:text-dark-tremor-brand",function(e,t){switch(e){case"line":return(0,o.q)("ui-selected:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","dark:hover:border-dark-tremor-content-emphasis dark:hover:text-dark-tremor-content-emphasis dark:text-dark-tremor-content",t?(0,s.bM)(t,i.K.border).selectBorderColor:"ui-selected:border-tremor-brand dark:ui-selected:border-dark-tremor-brand");case"solid":return(0,o.q)("border-transparent border rounded-tremor-small px-2.5 py-1","ui-selected:border-tremor-border ui-selected:bg-tremor-background ui-selected:shadow-tremor-input hover:text-tremor-content-emphasis ui-selected:text-tremor-brand","dark:ui-selected:border-dark-tremor-border dark:ui-selected:bg-dark-tremor-background dark:ui-selected:shadow-dark-tremor-input dark:hover:text-dark-tremor-content-emphasis dark:ui-selected:text-dark-tremor-brand",t?(0,s.bM)(t,i.K.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,f),p)},m),n?l.createElement(n,{className:(0,o.q)(u("icon"),"flex-none h-5 w-5",g?"mr-2":"")}):null,g?l.createElement("span",null,g):null)});p.displayName="Tab"},26734:function(e,t,n){n.d(t,{Z:function(){return c}});var a=n(69703),r=n(80991),i=n(99250),o=n(65492),s=n(64090);let l=(0,o.fn)("TabGroup"),c=s.forwardRef((e,t)=>{let{defaultIndex:n,index:o,onIndexChange:c,children:d,className:u}=e,p=(0,a._T)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.createElement(r.O.Group,Object.assign({as:"div",ref:t,defaultIndex:n,selectedIndex:o,onChange:c,className:(0,i.q)(l("root"),"w-full",u)},p),d)});c.displayName="TabGroup"},41608:function(e,t,n){n.d(t,{O:function(){return c},Z:function(){return u}});var a=n(69703),r=n(64090),i=n(50027);n(18174),n(21871),n(41213);var o=n(80991),s=n(99250);let l=(0,n(65492).fn)("TabList"),c=(0,r.createContext)("line"),d={line:(0,s.q)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,s.q)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},u=r.forwardRef((e,t)=>{let{color:n,variant:u="line",children:p,className:g}=e,m=(0,a._T)(e,["color","variant","children","className"]);return r.createElement(o.O.List,Object.assign({ref:t,className:(0,s.q)(l("root"),"justify-start overflow-x-clip",d[u],g)},m),r.createElement(c.Provider,{value:u},r.createElement(i.Z.Provider,{value:n},p)))});u.displayName="TabList"},32126:function(e,t,n){n.d(t,{Z:function(){return d}});var a=n(69703);n(50027);var r=n(18174);n(21871);var i=n(41213),o=n(99250),s=n(65492),l=n(64090);let c=(0,s.fn)("TabPanel"),d=l.forwardRef((e,t)=>{let{children:n,className:s}=e,d=(0,a._T)(e,["children","className"]),{selectedValue:u}=(0,l.useContext)(i.Z),p=u===(0,l.useContext)(r.Z);return l.createElement("div",Object.assign({ref:t,className:(0,o.q)(c("root"),"w-full mt-2",p?"":"hidden",s),"aria-selected":p?"true":"false"},d),n)});d.displayName="TabPanel"},23682:function(e,t,n){n.d(t,{Z:function(){return u}});var a=n(69703),r=n(80991);n(50027);var i=n(18174);n(21871);var o=n(41213),s=n(99250),l=n(65492),c=n(64090);let d=(0,l.fn)("TabPanels"),u=c.forwardRef((e,t)=>{let{children:n,className:l}=e,u=(0,a._T)(e,["children","className"]);return c.createElement(r.O.Panels,Object.assign({as:"div",ref:t,className:(0,s.q)(d("root"),"w-full",l)},u),e=>{let{selectedIndex:t}=e;return c.createElement(o.Z.Provider,{value:{selectedValue:t}},c.Children.map(n,(e,t)=>c.createElement(i.Z.Provider,{value:t},e)))})});u.displayName="TabPanels"},50027:function(e,t,n){n.d(t,{Z:function(){return i}});var a=n(64090),r=n(54942);n(99250);let i=(0,a.createContext)(r.fr.Blue)},18174:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)(0)},21871:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)(void 0)},41213:function(e,t,n){n.d(t,{Z:function(){return a}});let a=(0,n(64090).createContext)({selectedValue:void 0,handleValueChange:void 0})},21467:function(e,t,n){n.d(t,{i:function(){return s}});var a=n(64090),r=n(44329),i=n(54165),o=n(57499);function s(e){return t=>a.createElement(i.ZP,{theme:{token:{motion:!1,zIndexPopupBase:0}}},a.createElement(e,Object.assign({},t)))}t.Z=(e,t,n,i)=>s(s=>{let{prefixCls:l,style:c}=s,d=a.useRef(null),[u,p]=a.useState(0),[g,m]=a.useState(0),[b,f]=(0,r.Z)(!1,{value:s.open}),{getPrefixCls:E}=a.useContext(o.E_),h=E(t||"select",l);a.useEffect(()=>{if(f(!0),"undefined"!=typeof ResizeObserver){let e=new ResizeObserver(e=>{let t=e[0].target;p(t.offsetHeight+8),m(t.offsetWidth)}),t=setInterval(()=>{var a;let r=n?".".concat(n(h)):".".concat(h,"-dropdown"),i=null===(a=d.current)||void 0===a?void 0:a.querySelector(r);i&&(clearInterval(t),e.observe(i))},10);return()=>{clearInterval(t),e.disconnect()}}},[]);let S=Object.assign(Object.assign({},s),{style:Object.assign(Object.assign({},c),{margin:0}),open:b,visible:b,getPopupContainer:()=>d.current});return i&&(S=i(S)),a.createElement("div",{ref:d,style:{paddingBottom:u,position:"relative",minWidth:g}},a.createElement(e,Object.assign({},S)))})},99129:function(e,t,n){let a;n.d(t,{Z:function(){return eY}});var r=n(63787),i=n(64090),o=n(37274),s=n(57499),l=n(54165),c=n(99537),d=n(77136),u=n(20653),p=n(40388),g=n(16480),m=n.n(g),b=n(51761),f=n(47387),E=n(70595),h=n(24750),S=n(89211),y=n(1861),T=n(51350),A=e=>{let{type:t,children:n,prefixCls:a,buttonProps:r,close:o,autoFocus:s,emitEvent:l,isSilent:c,quitOnNullishReturnValue:d,actionFn:u}=e,p=i.useRef(!1),g=i.useRef(null),[m,b]=(0,S.Z)(!1),f=function(){null==o||o.apply(void 0,arguments)};i.useEffect(()=>{let e=null;return s&&(e=setTimeout(()=>{var e;null===(e=g.current)||void 0===e||e.focus()})),()=>{e&&clearTimeout(e)}},[]);let E=e=>{e&&e.then&&(b(!0),e.then(function(){b(!1,!0),f.apply(void 0,arguments),p.current=!1},e=>{if(b(!1,!0),p.current=!1,null==c||!c())return Promise.reject(e)}))};return i.createElement(y.ZP,Object.assign({},(0,T.nx)(t),{onClick:e=>{let t;if(!p.current){if(p.current=!0,!u){f();return}if(l){var n;if(t=u(e),d&&!((n=t)&&n.then)){p.current=!1,f(e);return}}else if(u.length)t=u(o),p.current=!1;else if(!(t=u())){f();return}E(t)}},loading:m,prefixCls:a},r,{ref:g}),n)};let R=i.createContext({}),{Provider:I}=R;var N=()=>{let{autoFocusButton:e,cancelButtonProps:t,cancelTextLocale:n,isSilent:a,mergedOkCancel:r,rootPrefixCls:o,close:s,onCancel:l,onConfirm:c}=(0,i.useContext)(R);return r?i.createElement(A,{isSilent:a,actionFn:l,close:function(){null==s||s.apply(void 0,arguments),null==c||c(!1)},autoFocus:"cancel"===e,buttonProps:t,prefixCls:"".concat(o,"-btn")},n):null},_=()=>{let{autoFocusButton:e,close:t,isSilent:n,okButtonProps:a,rootPrefixCls:r,okTextLocale:o,okType:s,onConfirm:l,onOk:c}=(0,i.useContext)(R);return i.createElement(A,{isSilent:n,type:s||"primary",actionFn:c,close:function(){null==t||t.apply(void 0,arguments),null==l||l(!0)},autoFocus:"ok"===e,buttonProps:a,prefixCls:"".concat(r,"-btn")},o)},v=n(81303),w=n(14749),k=n(80406),C=n(88804),O=i.createContext({}),x=n(5239),L=n(31506),D=n(91010),P=n(4295),M=n(72480);function F(e,t,n){var a=t;return!a&&n&&(a="".concat(e,"-").concat(n)),a}function U(e,t){var n=e["page".concat(t?"Y":"X","Offset")],a="scroll".concat(t?"Top":"Left");if("number"!=typeof n){var r=e.document;"number"!=typeof(n=r.documentElement[a])&&(n=r.body[a])}return n}var B=n(49367),G=n(74084),$=i.memo(function(e){return e.children},function(e,t){return!t.shouldUpdate}),H={width:0,height:0,overflow:"hidden",outline:"none"},z=i.forwardRef(function(e,t){var n,a,r,o=e.prefixCls,s=e.className,l=e.style,c=e.title,d=e.ariaId,u=e.footer,p=e.closable,g=e.closeIcon,b=e.onClose,f=e.children,E=e.bodyStyle,h=e.bodyProps,S=e.modalRender,y=e.onMouseDown,T=e.onMouseUp,A=e.holderRef,R=e.visible,I=e.forceRender,N=e.width,_=e.height,v=e.classNames,k=e.styles,C=i.useContext(O).panel,L=(0,G.x1)(A,C),D=(0,i.useRef)(),P=(0,i.useRef)();i.useImperativeHandle(t,function(){return{focus:function(){var e;null===(e=D.current)||void 0===e||e.focus()},changeActive:function(e){var t=document.activeElement;e&&t===P.current?D.current.focus():e||t!==D.current||P.current.focus()}}});var M={};void 0!==N&&(M.width=N),void 0!==_&&(M.height=_),u&&(n=i.createElement("div",{className:m()("".concat(o,"-footer"),null==v?void 0:v.footer),style:(0,x.Z)({},null==k?void 0:k.footer)},u)),c&&(a=i.createElement("div",{className:m()("".concat(o,"-header"),null==v?void 0:v.header),style:(0,x.Z)({},null==k?void 0:k.header)},i.createElement("div",{className:"".concat(o,"-title"),id:d},c))),p&&(r=i.createElement("button",{type:"button",onClick:b,"aria-label":"Close",className:"".concat(o,"-close")},g||i.createElement("span",{className:"".concat(o,"-close-x")})));var F=i.createElement("div",{className:m()("".concat(o,"-content"),null==v?void 0:v.content),style:null==k?void 0:k.content},r,a,i.createElement("div",(0,w.Z)({className:m()("".concat(o,"-body"),null==v?void 0:v.body),style:(0,x.Z)((0,x.Z)({},E),null==k?void 0:k.body)},h),f),n);return i.createElement("div",{key:"dialog-element",role:"dialog","aria-labelledby":c?d:null,"aria-modal":"true",ref:L,style:(0,x.Z)((0,x.Z)({},l),M),className:m()(o,s),onMouseDown:y,onMouseUp:T},i.createElement("div",{tabIndex:0,ref:D,style:H,"aria-hidden":"true"}),i.createElement($,{shouldUpdate:R||I},S?S(F):F),i.createElement("div",{tabIndex:0,ref:P,style:H,"aria-hidden":"true"}))}),j=i.forwardRef(function(e,t){var n=e.prefixCls,a=e.title,r=e.style,o=e.className,s=e.visible,l=e.forceRender,c=e.destroyOnClose,d=e.motionName,u=e.ariaId,p=e.onVisibleChanged,g=e.mousePosition,b=(0,i.useRef)(),f=i.useState(),E=(0,k.Z)(f,2),h=E[0],S=E[1],y={};function T(){var e,t,n,a,r,i=(n={left:(t=(e=b.current).getBoundingClientRect()).left,top:t.top},r=(a=e.ownerDocument).defaultView||a.parentWindow,n.left+=U(r),n.top+=U(r,!0),n);S(g?"".concat(g.x-i.left,"px ").concat(g.y-i.top,"px"):"")}return h&&(y.transformOrigin=h),i.createElement(B.ZP,{visible:s,onVisibleChanged:p,onAppearPrepare:T,onEnterPrepare:T,forceRender:l,motionName:d,removeOnLeave:c,ref:b},function(s,l){var c=s.className,d=s.style;return i.createElement(z,(0,w.Z)({},e,{ref:t,title:a,ariaId:u,prefixCls:n,holderRef:l,style:(0,x.Z)((0,x.Z)((0,x.Z)({},d),r),y),className:m()(o,c)}))})});function V(e){var t=e.prefixCls,n=e.style,a=e.visible,r=e.maskProps,o=e.motionName,s=e.className;return i.createElement(B.ZP,{key:"mask",visible:a,motionName:o,leavedClassName:"".concat(t,"-mask-hidden")},function(e,a){var o=e.className,l=e.style;return i.createElement("div",(0,w.Z)({ref:a,style:(0,x.Z)((0,x.Z)({},l),n),className:m()("".concat(t,"-mask"),o,s)},r))})}function W(e){var t=e.prefixCls,n=void 0===t?"rc-dialog":t,a=e.zIndex,r=e.visible,o=void 0!==r&&r,s=e.keyboard,l=void 0===s||s,c=e.focusTriggerAfterClose,d=void 0===c||c,u=e.wrapStyle,p=e.wrapClassName,g=e.wrapProps,b=e.onClose,f=e.afterOpenChange,E=e.afterClose,h=e.transitionName,S=e.animation,y=e.closable,T=e.mask,A=void 0===T||T,R=e.maskTransitionName,I=e.maskAnimation,N=e.maskClosable,_=e.maskStyle,v=e.maskProps,C=e.rootClassName,O=e.classNames,U=e.styles,B=(0,i.useRef)(),G=(0,i.useRef)(),$=(0,i.useRef)(),H=i.useState(o),z=(0,k.Z)(H,2),W=z[0],q=z[1],Y=(0,D.Z)();function K(e){null==b||b(e)}var Z=(0,i.useRef)(!1),X=(0,i.useRef)(),Q=null;return(void 0===N||N)&&(Q=function(e){Z.current?Z.current=!1:G.current===e.target&&K(e)}),(0,i.useEffect)(function(){o&&(q(!0),(0,L.Z)(G.current,document.activeElement)||(B.current=document.activeElement))},[o]),(0,i.useEffect)(function(){return function(){clearTimeout(X.current)}},[]),i.createElement("div",(0,w.Z)({className:m()("".concat(n,"-root"),C)},(0,M.Z)(e,{data:!0})),i.createElement(V,{prefixCls:n,visible:A&&o,motionName:F(n,R,I),style:(0,x.Z)((0,x.Z)({zIndex:a},_),null==U?void 0:U.mask),maskProps:v,className:null==O?void 0:O.mask}),i.createElement("div",(0,w.Z)({tabIndex:-1,onKeyDown:function(e){if(l&&e.keyCode===P.Z.ESC){e.stopPropagation(),K(e);return}o&&e.keyCode===P.Z.TAB&&$.current.changeActive(!e.shiftKey)},className:m()("".concat(n,"-wrap"),p,null==O?void 0:O.wrapper),ref:G,onClick:Q,style:(0,x.Z)((0,x.Z)((0,x.Z)({zIndex:a},u),null==U?void 0:U.wrapper),{},{display:W?null:"none"})},g),i.createElement(j,(0,w.Z)({},e,{onMouseDown:function(){clearTimeout(X.current),Z.current=!0},onMouseUp:function(){X.current=setTimeout(function(){Z.current=!1})},ref:$,closable:void 0===y||y,ariaId:Y,prefixCls:n,visible:o&&W,onClose:K,onVisibleChanged:function(e){if(e)!function(){if(!(0,L.Z)(G.current,document.activeElement)){var e;null===(e=$.current)||void 0===e||e.focus()}}();else{if(q(!1),A&&B.current&&d){try{B.current.focus({preventScroll:!0})}catch(e){}B.current=null}W&&(null==E||E())}null==f||f(e)},motionName:F(n,h,S)}))))}j.displayName="Content",n(53850);var q=function(e){var t=e.visible,n=e.getContainer,a=e.forceRender,r=e.destroyOnClose,o=void 0!==r&&r,s=e.afterClose,l=e.panelRef,c=i.useState(t),d=(0,k.Z)(c,2),u=d[0],p=d[1],g=i.useMemo(function(){return{panel:l}},[l]);return(i.useEffect(function(){t&&p(!0)},[t]),a||!o||u)?i.createElement(O.Provider,{value:g},i.createElement(C.Z,{open:t||a||u,autoDestroy:!1,getContainer:n,autoLock:t||u},i.createElement(W,(0,w.Z)({},e,{destroyOnClose:o,afterClose:function(){null==s||s(),p(!1)}})))):null};q.displayName="Dialog";var Y=function(e,t,n){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:i.createElement(v.Z,null),r=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if("boolean"==typeof e?!e:void 0===t?!r:!1===t||null===t)return[!1,null];let o="boolean"==typeof t||null==t?a:t;return[!0,n?n(o):o]},K=n(22127),Z=n(86718),X=n(47137),Q=n(92801),J=n(48563);function ee(){}let et=i.createContext({add:ee,remove:ee});var en=n(17094),ea=()=>{let{cancelButtonProps:e,cancelTextLocale:t,onCancel:n}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({onClick:n},e),t)},er=()=>{let{confirmLoading:e,okButtonProps:t,okType:n,okTextLocale:a,onOk:r}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({},(0,T.nx)(n),{loading:e,onClick:r},t),a)},ei=n(4678);function eo(e,t){return i.createElement("span",{className:"".concat(e,"-close-x")},t||i.createElement(v.Z,{className:"".concat(e,"-close-icon")}))}let es=e=>{let t;let{okText:n,okType:a="primary",cancelText:o,confirmLoading:s,onOk:l,onCancel:c,okButtonProps:d,cancelButtonProps:u,footer:p}=e,[g]=(0,E.Z)("Modal",(0,ei.A)()),m={confirmLoading:s,okButtonProps:d,cancelButtonProps:u,okTextLocale:n||(null==g?void 0:g.okText),cancelTextLocale:o||(null==g?void 0:g.cancelText),okType:a,onOk:l,onCancel:c},b=i.useMemo(()=>m,(0,r.Z)(Object.values(m)));return"function"==typeof p||void 0===p?(t=i.createElement(i.Fragment,null,i.createElement(ea,null),i.createElement(er,null)),"function"==typeof p&&(t=p(t,{OkBtn:er,CancelBtn:ea})),t=i.createElement(I,{value:b},t)):t=p,i.createElement(en.n,{disabled:!1},t)};var el=n(11303),ec=n(13703),ed=n(58854),eu=n(80316),ep=n(76585),eg=n(8985);function em(e){return{position:e,inset:0}}let eb=e=>{let{componentCls:t,antCls:n}=e;return[{["".concat(t,"-root")]:{["".concat(t).concat(n,"-zoom-enter, ").concat(t).concat(n,"-zoom-appear")]:{transform:"none",opacity:0,animationDuration:e.motionDurationSlow,userSelect:"none"},["".concat(t).concat(n,"-zoom-leave ").concat(t,"-content")]:{pointerEvents:"none"},["".concat(t,"-mask")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,height:"100%",backgroundColor:e.colorBgMask,pointerEvents:"none",["".concat(t,"-hidden")]:{display:"none"}}),["".concat(t,"-wrap")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,overflow:"auto",outline:0,WebkitOverflowScrolling:"touch",["&:has(".concat(t).concat(n,"-zoom-enter), &:has(").concat(t).concat(n,"-zoom-appear)")]:{pointerEvents:"none"}})}},{["".concat(t,"-root")]:(0,ec.J$)(e)}]},ef=e=>{let{componentCls:t}=e;return[{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl"},["".concat(t,"-centered")]:{textAlign:"center","&::before":{display:"inline-block",width:0,height:"100%",verticalAlign:"middle",content:'""'},[t]:{top:0,display:"inline-block",paddingBottom:0,textAlign:"start",verticalAlign:"middle"}},["@media (max-width: ".concat(e.screenSMMax,"px)")]:{[t]:{maxWidth:"calc(100vw - 16px)",margin:"".concat((0,eg.bf)(e.marginXS)," auto")},["".concat(t,"-centered")]:{[t]:{flex:1}}}}},{[t]:Object.assign(Object.assign({},(0,el.Wf)(e)),{pointerEvents:"none",position:"relative",top:100,width:"auto",maxWidth:"calc(100vw - ".concat((0,eg.bf)(e.calc(e.margin).mul(2).equal()),")"),margin:"0 auto",paddingBottom:e.paddingLG,["".concat(t,"-title")]:{margin:0,color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.titleFontSize,lineHeight:e.titleLineHeight,wordWrap:"break-word"},["".concat(t,"-content")]:{position:"relative",backgroundColor:e.contentBg,backgroundClip:"padding-box",border:0,borderRadius:e.borderRadiusLG,boxShadow:e.boxShadow,pointerEvents:"auto",padding:e.contentPadding},["".concat(t,"-close")]:Object.assign({position:"absolute",top:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),insetInlineEnd:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),zIndex:e.calc(e.zIndexPopupBase).add(10).equal(),padding:0,color:e.modalCloseIconColor,fontWeight:e.fontWeightStrong,lineHeight:1,textDecoration:"none",background:"transparent",borderRadius:e.borderRadiusSM,width:e.modalCloseBtnSize,height:e.modalCloseBtnSize,border:0,outline:0,cursor:"pointer",transition:"color ".concat(e.motionDurationMid,", background-color ").concat(e.motionDurationMid),"&-x":{display:"flex",fontSize:e.fontSizeLG,fontStyle:"normal",lineHeight:"".concat((0,eg.bf)(e.modalCloseBtnSize)),justifyContent:"center",textTransform:"none",textRendering:"auto"},"&:hover":{color:e.modalIconHoverColor,backgroundColor:e.closeBtnHoverBg,textDecoration:"none"},"&:active":{backgroundColor:e.closeBtnActiveBg}},(0,el.Qy)(e)),["".concat(t,"-header")]:{color:e.colorText,background:e.headerBg,borderRadius:"".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)," 0 0"),marginBottom:e.headerMarginBottom,padding:e.headerPadding,borderBottom:e.headerBorderBottom},["".concat(t,"-body")]:{fontSize:e.fontSize,lineHeight:e.lineHeight,wordWrap:"break-word",padding:e.bodyPadding},["".concat(t,"-footer")]:{textAlign:"end",background:e.footerBg,marginTop:e.footerMarginTop,padding:e.footerPadding,borderTop:e.footerBorderTop,borderRadius:e.footerBorderRadius,["> ".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginInlineStart:e.marginXS}},["".concat(t,"-open")]:{overflow:"hidden"}})},{["".concat(t,"-pure-panel")]:{top:"auto",padding:0,display:"flex",flexDirection:"column",["".concat(t,"-content,\n ").concat(t,"-body,\n ").concat(t,"-confirm-body-wrapper")]:{display:"flex",flexDirection:"column",flex:"auto"},["".concat(t,"-confirm-body")]:{marginBottom:"auto"}}}]},eE=e=>{let{componentCls:t}=e;return{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl",["".concat(t,"-confirm-body")]:{direction:"rtl"}}}}},eh=e=>{let t=e.padding,n=e.fontSizeHeading5,a=e.lineHeightHeading5;return(0,eu.TS)(e,{modalHeaderHeight:e.calc(e.calc(a).mul(n).equal()).add(e.calc(t).mul(2).equal()).equal(),modalFooterBorderColorSplit:e.colorSplit,modalFooterBorderStyle:e.lineType,modalFooterBorderWidth:e.lineWidth,modalIconHoverColor:e.colorIconHover,modalCloseIconColor:e.colorIcon,modalCloseBtnSize:e.fontHeight,modalConfirmIconSize:e.fontHeight,modalTitleHeight:e.calc(e.titleFontSize).mul(e.titleLineHeight).equal()})},eS=e=>({footerBg:"transparent",headerBg:e.colorBgElevated,titleLineHeight:e.lineHeightHeading5,titleFontSize:e.fontSizeHeading5,contentBg:e.colorBgElevated,titleColor:e.colorTextHeading,closeBtnHoverBg:e.wireframe?"transparent":e.colorFillContent,closeBtnActiveBg:e.wireframe?"transparent":e.colorFillContentHover,contentPadding:e.wireframe?0:"".concat((0,eg.bf)(e.paddingMD)," ").concat((0,eg.bf)(e.paddingContentHorizontalLG)),headerPadding:e.wireframe?"".concat((0,eg.bf)(e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,headerBorderBottom:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",headerMarginBottom:e.wireframe?0:e.marginXS,bodyPadding:e.wireframe?e.paddingLG:0,footerPadding:e.wireframe?"".concat((0,eg.bf)(e.paddingXS)," ").concat((0,eg.bf)(e.padding)):0,footerBorderTop:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",footerBorderRadius:e.wireframe?"0 0 ".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)):0,footerMarginTop:e.wireframe?0:e.marginSM,confirmBodyPadding:e.wireframe?"".concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,confirmIconMarginInlineEnd:e.wireframe?e.margin:e.marginSM,confirmBtnsMarginTop:e.wireframe?e.marginLG:e.marginSM});var ey=(0,ep.I$)("Modal",e=>{let t=eh(e);return[ef(t),eE(t),eb(t),(0,ed._y)(t,"zoom")]},eS,{unitless:{titleLineHeight:!0}}),eT=n(92935),eA=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};(0,K.Z)()&&window.document.documentElement&&document.documentElement.addEventListener("click",e=>{a={x:e.pageX,y:e.pageY},setTimeout(()=>{a=null},100)},!0);var eR=e=>{var t;let{getPopupContainer:n,getPrefixCls:r,direction:o,modal:l}=i.useContext(s.E_),c=t=>{let{onCancel:n}=e;null==n||n(t)},{prefixCls:d,className:u,rootClassName:p,open:g,wrapClassName:E,centered:h,getContainer:S,closeIcon:y,closable:T,focusTriggerAfterClose:A=!0,style:R,visible:I,width:N=520,footer:_,classNames:w,styles:k}=e,C=eA(e,["prefixCls","className","rootClassName","open","wrapClassName","centered","getContainer","closeIcon","closable","focusTriggerAfterClose","style","visible","width","footer","classNames","styles"]),O=r("modal",d),x=r(),L=(0,eT.Z)(O),[D,P,M]=ey(O,L),F=m()(E,{["".concat(O,"-centered")]:!!h,["".concat(O,"-wrap-rtl")]:"rtl"===o}),U=null!==_&&i.createElement(es,Object.assign({},e,{onOk:t=>{let{onOk:n}=e;null==n||n(t)},onCancel:c})),[B,G]=Y(T,y,e=>eo(O,e),i.createElement(v.Z,{className:"".concat(O,"-close-icon")}),!0),$=function(e){let t=i.useContext(et),n=i.useRef();return(0,J.zX)(a=>{if(a){let r=e?a.querySelector(e):a;t.add(r),n.current=r}else t.remove(n.current)})}(".".concat(O,"-content")),[H,z]=(0,b.Cn)("Modal",C.zIndex);return D(i.createElement(Q.BR,null,i.createElement(X.Ux,{status:!0,override:!0},i.createElement(Z.Z.Provider,{value:z},i.createElement(q,Object.assign({width:N},C,{zIndex:H,getContainer:void 0===S?n:S,prefixCls:O,rootClassName:m()(P,p,M,L),footer:U,visible:null!=g?g:I,mousePosition:null!==(t=C.mousePosition)&&void 0!==t?t:a,onClose:c,closable:B,closeIcon:G,focusTriggerAfterClose:A,transitionName:(0,f.m)(x,"zoom",e.transitionName),maskTransitionName:(0,f.m)(x,"fade",e.maskTransitionName),className:m()(P,u,null==l?void 0:l.className),style:Object.assign(Object.assign({},null==l?void 0:l.style),R),classNames:Object.assign(Object.assign({wrapper:F},null==l?void 0:l.classNames),w),styles:Object.assign(Object.assign({},null==l?void 0:l.styles),k),panelRef:$}))))))};let eI=e=>{let{componentCls:t,titleFontSize:n,titleLineHeight:a,modalConfirmIconSize:r,fontSize:i,lineHeight:o,modalTitleHeight:s,fontHeight:l,confirmBodyPadding:c}=e,d="".concat(t,"-confirm");return{[d]:{"&-rtl":{direction:"rtl"},["".concat(e.antCls,"-modal-header")]:{display:"none"},["".concat(d,"-body-wrapper")]:Object.assign({},(0,el.dF)()),["&".concat(t," ").concat(t,"-body")]:{padding:c},["".concat(d,"-body")]:{display:"flex",flexWrap:"nowrap",alignItems:"start",["> ".concat(e.iconCls)]:{flex:"none",fontSize:r,marginInlineEnd:e.confirmIconMarginInlineEnd,marginTop:e.calc(e.calc(l).sub(r).equal()).div(2).equal()},["&-has-title > ".concat(e.iconCls)]:{marginTop:e.calc(e.calc(s).sub(r).equal()).div(2).equal()}},["".concat(d,"-paragraph")]:{display:"flex",flexDirection:"column",flex:"auto",rowGap:e.marginXS,maxWidth:"calc(100% - ".concat((0,eg.bf)(e.calc(e.modalConfirmIconSize).add(e.marginSM).equal()),")")},["".concat(d,"-title")]:{color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:n,lineHeight:a},["".concat(d,"-content")]:{color:e.colorText,fontSize:i,lineHeight:o},["".concat(d,"-btns")]:{textAlign:"end",marginTop:e.confirmBtnsMarginTop,["".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginBottom:0,marginInlineStart:e.marginXS}}},["".concat(d,"-error ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorError},["".concat(d,"-warning ").concat(d,"-body > ").concat(e.iconCls,",\n ").concat(d,"-confirm ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorWarning},["".concat(d,"-info ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorInfo},["".concat(d,"-success ").concat(d,"-body > ").concat(e.iconCls)]:{color:e.colorSuccess}}};var eN=(0,ep.bk)(["Modal","confirm"],e=>[eI(eh(e))],eS,{order:-1e3}),e_=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};function ev(e){let{prefixCls:t,icon:n,okText:a,cancelText:o,confirmPrefixCls:s,type:l,okCancel:g,footer:b,locale:f}=e,h=e_(e,["prefixCls","icon","okText","cancelText","confirmPrefixCls","type","okCancel","footer","locale"]),S=n;if(!n&&null!==n)switch(l){case"info":S=i.createElement(p.Z,null);break;case"success":S=i.createElement(c.Z,null);break;case"error":S=i.createElement(d.Z,null);break;default:S=i.createElement(u.Z,null)}let y=null!=g?g:"confirm"===l,T=null!==e.autoFocusButton&&(e.autoFocusButton||"ok"),[A]=(0,E.Z)("Modal"),R=f||A,v=a||(y?null==R?void 0:R.okText:null==R?void 0:R.justOkText),w=Object.assign({autoFocusButton:T,cancelTextLocale:o||(null==R?void 0:R.cancelText),okTextLocale:v,mergedOkCancel:y},h),k=i.useMemo(()=>w,(0,r.Z)(Object.values(w))),C=i.createElement(i.Fragment,null,i.createElement(N,null),i.createElement(_,null)),O=void 0!==e.title&&null!==e.title,x="".concat(s,"-body");return i.createElement("div",{className:"".concat(s,"-body-wrapper")},i.createElement("div",{className:m()(x,{["".concat(x,"-has-title")]:O})},S,i.createElement("div",{className:"".concat(s,"-paragraph")},O&&i.createElement("span",{className:"".concat(s,"-title")},e.title),i.createElement("div",{className:"".concat(s,"-content")},e.content))),void 0===b||"function"==typeof b?i.createElement(I,{value:k},i.createElement("div",{className:"".concat(s,"-btns")},"function"==typeof b?b(C,{OkBtn:_,CancelBtn:N}):C)):b,i.createElement(eN,{prefixCls:t}))}let ew=e=>{let{close:t,zIndex:n,afterClose:a,open:r,keyboard:o,centered:s,getContainer:l,maskStyle:c,direction:d,prefixCls:u,wrapClassName:p,rootPrefixCls:g,bodyStyle:E,closable:S=!1,closeIcon:y,modalRender:T,focusTriggerAfterClose:A,onConfirm:R,styles:I}=e,N="".concat(u,"-confirm"),_=e.width||416,v=e.style||{},w=void 0===e.mask||e.mask,k=void 0!==e.maskClosable&&e.maskClosable,C=m()(N,"".concat(N,"-").concat(e.type),{["".concat(N,"-rtl")]:"rtl"===d},e.className),[,O]=(0,h.ZP)(),x=i.useMemo(()=>void 0!==n?n:O.zIndexPopupBase+b.u6,[n,O]);return i.createElement(eR,{prefixCls:u,className:C,wrapClassName:m()({["".concat(N,"-centered")]:!!e.centered},p),onCancel:()=>{null==t||t({triggerCancel:!0}),null==R||R(!1)},open:r,title:"",footer:null,transitionName:(0,f.m)(g||"","zoom",e.transitionName),maskTransitionName:(0,f.m)(g||"","fade",e.maskTransitionName),mask:w,maskClosable:k,style:v,styles:Object.assign({body:E,mask:c},I),width:_,zIndex:x,afterClose:a,keyboard:o,centered:s,getContainer:l,closable:S,closeIcon:y,modalRender:T,focusTriggerAfterClose:A},i.createElement(ev,Object.assign({},e,{confirmPrefixCls:N})))};var ek=e=>{let{rootPrefixCls:t,iconPrefixCls:n,direction:a,theme:r}=e;return i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:n,direction:a,theme:r},i.createElement(ew,Object.assign({},e)))},eC=[];let eO="",ex=e=>{var t,n;let{prefixCls:a,getContainer:r,direction:o}=e,l=(0,ei.A)(),c=(0,i.useContext)(s.E_),d=eO||c.getPrefixCls(),u=a||"".concat(d,"-modal"),p=r;return!1===p&&(p=void 0),i.createElement(ek,Object.assign({},e,{rootPrefixCls:d,prefixCls:u,iconPrefixCls:c.iconPrefixCls,theme:c.theme,direction:null!=o?o:c.direction,locale:null!==(n=null===(t=c.locale)||void 0===t?void 0:t.Modal)&&void 0!==n?n:l,getContainer:p}))};function eL(e){let t;let n=(0,l.w6)(),a=document.createDocumentFragment(),s=Object.assign(Object.assign({},e),{close:u,open:!0});function c(){for(var t=arguments.length,n=Array(t),i=0;ie&&e.triggerCancel);e.onCancel&&s&&e.onCancel.apply(e,[()=>{}].concat((0,r.Z)(n.slice(1))));for(let e=0;e{let t=n.getPrefixCls(void 0,eO),r=n.getIconPrefixCls(),s=n.getTheme(),c=i.createElement(ex,Object.assign({},e));(0,o.s)(i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:r,theme:s},n.holderRender?n.holderRender(c):c),a)})}function u(){for(var t=arguments.length,n=Array(t),a=0;a{"function"==typeof e.afterClose&&e.afterClose(),c.apply(this,n)}})).visible&&delete s.visible,d(s)}return d(s),eC.push(u),{destroy:u,update:function(e){d(s="function"==typeof e?e(s):Object.assign(Object.assign({},s),e))}}}function eD(e){return Object.assign(Object.assign({},e),{type:"warning"})}function eP(e){return Object.assign(Object.assign({},e),{type:"info"})}function eM(e){return Object.assign(Object.assign({},e),{type:"success"})}function eF(e){return Object.assign(Object.assign({},e),{type:"error"})}function eU(e){return Object.assign(Object.assign({},e),{type:"confirm"})}var eB=n(21467),eG=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},e$=(0,eB.i)(e=>{let{prefixCls:t,className:n,closeIcon:a,closable:r,type:o,title:l,children:c,footer:d}=e,u=eG(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:p}=i.useContext(s.E_),g=p(),b=t||p("modal"),f=(0,eT.Z)(g),[E,h,S]=ey(b,f),y="".concat(b,"-confirm"),T={};return T=o?{closable:null!=r&&r,title:"",footer:"",children:i.createElement(ev,Object.assign({},e,{prefixCls:b,confirmPrefixCls:y,rootPrefixCls:g,content:c}))}:{closable:null==r||r,title:l,footer:null!==d&&i.createElement(es,Object.assign({},e)),children:c},E(i.createElement(z,Object.assign({prefixCls:b,className:m()(h,"".concat(b,"-pure-panel"),o&&y,o&&"".concat(y,"-").concat(o),n,S,f)},u,{closeIcon:eo(b,a),closable:r},T)))}),eH=n(79474),ez=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},ej=i.forwardRef((e,t)=>{var n,{afterClose:a,config:o}=e,l=ez(e,["afterClose","config"]);let[c,d]=i.useState(!0),[u,p]=i.useState(o),{direction:g,getPrefixCls:m}=i.useContext(s.E_),b=m("modal"),f=m(),h=function(){d(!1);for(var e=arguments.length,t=Array(e),n=0;ne&&e.triggerCancel);u.onCancel&&a&&u.onCancel.apply(u,[()=>{}].concat((0,r.Z)(t.slice(1))))};i.useImperativeHandle(t,()=>({destroy:h,update:e=>{p(t=>Object.assign(Object.assign({},t),e))}}));let S=null!==(n=u.okCancel)&&void 0!==n?n:"confirm"===u.type,[y]=(0,E.Z)("Modal",eH.Z.Modal);return i.createElement(ek,Object.assign({prefixCls:b,rootPrefixCls:f},u,{close:h,open:c,afterClose:()=>{var e;a(),null===(e=u.afterClose)||void 0===e||e.call(u)},okText:u.okText||(S?null==y?void 0:y.okText:null==y?void 0:y.justOkText),direction:u.direction||g,cancelText:u.cancelText||(null==y?void 0:y.cancelText)},l))});let eV=0,eW=i.memo(i.forwardRef((e,t)=>{let[n,a]=function(){let[e,t]=i.useState([]);return[e,i.useCallback(e=>(t(t=>[].concat((0,r.Z)(t),[e])),()=>{t(t=>t.filter(t=>t!==e))}),[])]}();return i.useImperativeHandle(t,()=>({patchElement:a}),[]),i.createElement(i.Fragment,null,n)}));function eq(e){return eL(eD(e))}eR.useModal=function(){let e=i.useRef(null),[t,n]=i.useState([]);i.useEffect(()=>{t.length&&((0,r.Z)(t).forEach(e=>{e()}),n([]))},[t]);let a=i.useCallback(t=>function(a){var o;let s,l;eV+=1;let c=i.createRef(),d=new Promise(e=>{s=e}),u=!1,p=i.createElement(ej,{key:"modal-".concat(eV),config:t(a),ref:c,afterClose:()=>{null==l||l()},isSilent:()=>u,onConfirm:e=>{s(e)}});return(l=null===(o=e.current)||void 0===o?void 0:o.patchElement(p))&&eC.push(l),{destroy:()=>{function e(){var e;null===(e=c.current)||void 0===e||e.destroy()}c.current?e():n(t=>[].concat((0,r.Z)(t),[e]))},update:e=>{function t(){var t;null===(t=c.current)||void 0===t||t.update(e)}c.current?t():n(e=>[].concat((0,r.Z)(e),[t]))},then:e=>(u=!0,d.then(e))}},[]);return[i.useMemo(()=>({info:a(eP),success:a(eM),error:a(eF),warning:a(eD),confirm:a(eU)}),[]),i.createElement(eW,{key:"modal-holder",ref:e})]},eR.info=function(e){return eL(eP(e))},eR.success=function(e){return eL(eM(e))},eR.error=function(e){return eL(eF(e))},eR.warning=eq,eR.warn=eq,eR.confirm=function(e){return eL(eU(e))},eR.destroyAll=function(){for(;eC.length;){let e=eC.pop();e&&e()}},eR.config=function(e){let{rootPrefixCls:t}=e;eO=t},eR._InternalPanelDoNotUseOrYouWillBeFired=e$;var eY=eR},13703:function(e,t,n){n.d(t,{J$:function(){return s}});var a=n(8985),r=n(59353);let i=new a.E4("antFadeIn",{"0%":{opacity:0},"100%":{opacity:1}}),o=new a.E4("antFadeOut",{"0%":{opacity:1},"100%":{opacity:0}}),s=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],{antCls:n}=e,a="".concat(n,"-fade"),s=t?"&":"";return[(0,r.R)(a,i,o,e.motionDurationMid,t),{["\n ".concat(s).concat(a,"-enter,\n ").concat(s).concat(a,"-appear\n ")]:{opacity:0,animationTimingFunction:"linear"},["".concat(s).concat(a,"-leave")]:{animationTimingFunction:"linear"}}]}},44056:function(e){e.exports=function(e,n){for(var a,r,i,o=e||"",s=n||"div",l={},c=0;c4&&m.slice(0,4)===o&&s.test(t)&&("-"===t.charAt(4)?b=o+(n=t.slice(5).replace(l,u)).charAt(0).toUpperCase()+n.slice(1):(g=(p=t).slice(4),t=l.test(g)?p:("-"!==(g=g.replace(c,d)).charAt(0)&&(g="-"+g),o+g)),f=r),new f(b,t))};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,c=/[A-Z]/g;function d(e){return"-"+e.toLowerCase()}function u(e){return e.charAt(1).toUpperCase()}},31872:function(e,t,n){var a=n(96130),r=n(64730),i=n(61861),o=n(46982),s=n(83671),l=n(53618);e.exports=a([i,r,o,s,l])},83671:function(e,t,n){var a=n(7667),r=n(13585),i=a.booleanish,o=a.number,s=a.spaceSeparated;e.exports=r({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:i,ariaAutoComplete:null,ariaBusy:i,ariaChecked:i,ariaColCount:o,ariaColIndex:o,ariaColSpan:o,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:i,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:i,ariaFlowTo:s,ariaGrabbed:i,ariaHasPopup:null,ariaHidden:i,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:o,ariaLive:null,ariaModal:i,ariaMultiLine:i,ariaMultiSelectable:i,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:o,ariaPressed:i,ariaReadOnly:i,ariaRelevant:null,ariaRequired:i,ariaRoleDescription:s,ariaRowCount:o,ariaRowIndex:o,ariaRowSpan:o,ariaSelected:i,ariaSetSize:o,ariaSort:null,ariaValueMax:o,ariaValueMin:o,ariaValueNow:o,ariaValueText:null,role:null}})},53618:function(e,t,n){var a=n(7667),r=n(13585),i=n(46640),o=a.boolean,s=a.overloadedBoolean,l=a.booleanish,c=a.number,d=a.spaceSeparated,u=a.commaSeparated;e.exports=r({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:i,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:u,acceptCharset:d,accessKey:d,action:null,allow:null,allowFullScreen:o,allowPaymentRequest:o,allowUserMedia:o,alt:null,as:null,async:o,autoCapitalize:null,autoComplete:d,autoFocus:o,autoPlay:o,capture:o,charSet:null,checked:o,cite:null,className:d,cols:c,colSpan:null,content:null,contentEditable:l,controls:o,controlsList:d,coords:c|u,crossOrigin:null,data:null,dateTime:null,decoding:null,default:o,defer:o,dir:null,dirName:null,disabled:o,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:o,formTarget:null,headers:d,height:c,hidden:o,high:c,href:null,hrefLang:null,htmlFor:d,httpEquiv:d,id:null,imageSizes:null,imageSrcSet:u,inputMode:null,integrity:null,is:null,isMap:o,itemId:null,itemProp:d,itemRef:d,itemScope:o,itemType:d,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:o,low:c,manifest:null,max:null,maxLength:c,media:null,method:null,min:null,minLength:c,multiple:o,muted:o,name:null,nonce:null,noModule:o,noValidate:o,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:o,optimum:c,pattern:null,ping:d,placeholder:null,playsInline:o,poster:null,preload:null,readOnly:o,referrerPolicy:null,rel:d,required:o,reversed:o,rows:c,rowSpan:c,sandbox:d,scope:null,scoped:o,seamless:o,selected:o,shape:null,size:c,sizes:null,slot:null,span:c,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:u,start:c,step:null,style:null,tabIndex:c,target:null,title:null,translate:null,type:null,typeMustMatch:o,useMap:null,value:l,width:c,wrap:null,align:null,aLink:null,archive:d,axis:null,background:null,bgColor:null,border:c,borderColor:null,bottomMargin:c,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:o,declare:o,event:null,face:null,frame:null,frameBorder:null,hSpace:c,leftMargin:c,link:null,longDesc:null,lowSrc:null,marginHeight:c,marginWidth:c,noResize:o,noHref:o,noShade:o,noWrap:o,object:null,profile:null,prompt:null,rev:null,rightMargin:c,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:c,valueType:null,version:null,vAlign:null,vLink:null,vSpace:c,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:o,disableRemotePlayback:o,prefix:null,property:null,results:c,security:null,unselectable:null}})},46640:function(e,t,n){var a=n(25852);e.exports=function(e,t){return a(e,t.toLowerCase())}},25852:function(e){e.exports=function(e,t){return t in e?e[t]:t}},13585:function(e,t,n){var a=n(39900),r=n(94949),i=n(7478);e.exports=function(e){var t,n,o=e.space,s=e.mustUseProperty||[],l=e.attributes||{},c=e.properties,d=e.transform,u={},p={};for(t in c)n=new i(t,d(l,t),c[t],o),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),u[t]=n,p[a(t)]=t,p[a(n.attribute)]=t;return new r(u,p,o)}},7478:function(e,t,n){var a=n(74108),r=n(7667);e.exports=s,s.prototype=new a,s.prototype.defined=!0;var i=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],o=i.length;function s(e,t,n,s){var l,c,d,u=-1;for(s&&(this.space=s),a.call(this,e,t);++u