From f55fb17d4004b4814a993767ac89d66f9f930a5c Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 02:27:30 -0800 Subject: [PATCH 1/8] migrate opensearch to clickhouse --- .gitignore | 4 +- .vscode/settings.json | 17 +- README.md | 2 +- apps/console/project.json | 2 +- apps/console/src/app.tsx | 2 - .../src/assets/providers/anthropic-logo.png | Bin 0 -> 667 bytes .../src/assets/providers/anthropic-logo.svg | 1 - .../src/assets/providers/azure-logo.png | Bin 0 -> 1353 bytes .../src/assets/providers/azure-logo.svg | 23 - .../src/assets/providers/meta-logo.png | Bin 0 -> 2527 bytes .../src/assets/providers/mistral-logo.png | Bin 0 -> 1392 bytes .../src/assets/providers/openai-logo.png | Bin 0 -> 1303 bytes .../src/assets/providers/openai-logo.svg | 1 - .../src/components/metrics/StatisticBox.tsx | 3 +- .../editor/ProviderSelector/providers.tsx | 12 +- .../prompt-tester/PromptTesterModal.tsx | 15 +- .../prompts/views/PromptMetricsView.tsx | 60 - .../components/requests/RequestDetails.tsx | 121 +- .../requests/RequestResponseViewJsonView.tsx | 16 +- .../requests/filters/FilterItem.tsx | 24 +- .../chat/normalizers/openai-normalizer.tsx | 2 - .../graphql/definitions/queries/metrics.tsx | 30 +- .../definitions/queries/prompt-executions.tsx | 9 +- .../graphql/definitions/queries/requests.ts | 17 +- apps/console/src/graphql/hooks/queries.ts | 78 +- apps/console/src/graphql/types.ts | 17 +- apps/console/src/lib/constants/filters.ts | 19 +- .../src/lib/hooks/useFiltersAndSortParams.ts | 2 +- .../lib/hooks/useGetPromptExecutionMetric.ts | 16 - .../src/lib/providers/MetricContext.tsx | 186 --- .../src/lib/providers/PromptTesterContext.tsx | 4 +- .../pages/projects/overview/DashboardPage.tsx | 21 +- .../projects/overview/StatisticsSection.tsx | 5 +- .../overview/charts/ExecutionTimeChart.tsx | 35 +- .../overview/charts/ModelUsageChart.tsx | 172 +++ .../overview/charts/ProjectMetricContext.tsx | 2 +- .../overview/charts/SuccessErrorRateChart.tsx | 65 +- .../overview/charts/TooltipWithTimestamp.tsx | 4 +- .../overview/useProjectOverviewMetrics.tsx | 36 +- .../src/pages/prompts/PromptNavigation.tsx | 10 +- .../src/pages/requests/ModelDetails.tsx | 20 + .../src/pages/requests/RequestsPage.tsx | 79 +- .../pages/requests/model-display-details.tsx | 34 + apps/console/src/pages/requests/types.ts | 2 + apps/console/tsconfig.app.json | 2 +- apps/proxy/project.json | 2 +- apps/proxy/src/lib/OpenAIHandler.ts | 2 +- apps/server/Dockerfile | 4 + apps/server/project.json | 2 +- .../server/scripts/generate-graphql-schema.ts | 4 +- apps/server/src/app/app.module.ts | 4 +- .../src/app/clickhouse/clickhouse.module.ts | 10 + .../src/app/clickhouse/clickhouse.service.ts | 66 + .../src/app/common/filters/filter.input.ts | 27 +- apps/server/src/app/common/filters/shared.ts | 12 +- .../src/app/config/common-config-schema.ts | 8 +- .../inputs/get-project-metrics.input.ts | 74 +- .../inputs/get-prompt-metrics.input.ts | 62 - apps/server/src/app/metrics/metrics.module.ts | 6 +- .../src/app/metrics/metrics.resolver.ts | 146 -- .../src/app/metrics/models/metric.model.ts | 15 - .../metrics/models/project-metric.model.ts | 39 +- .../app/metrics/project-metrics.resolver.ts | 90 +- .../app/metrics/project-metrics.service.ts | 338 ++--- .../queries/average-request-duration-query.ts | 37 + .../app/metrics/queries/model-usage-query.ts | 43 + .../src/app/metrics/queries/queries.utils.ts | 51 + .../queries/success-error-rate-query.ts | 42 + .../src/app/opensearch/create-indexes.ts | 145 -- .../src/app/opensearch/opensearch.module.ts | 10 - .../src/app/opensearch/opensearch.service.ts | 68 - .../prompt-tester/prompt-tester.resolver.ts | 6 +- .../prompt-tester/prompt-tester.service.ts | 4 +- .../app/prompts/prompt-versions.resolver.ts | 1 - .../app/reporting/dto/create-report.dto.ts | 36 +- .../reporting/inputs/get-requests.input.ts | 9 + .../request-report-result.model.ts | 16 +- .../src/app/reporting/reporting.controller.ts | 4 +- .../src/app/reporting/reporting.module.ts | 4 +- .../src/app/reporting/reporting.service.ts | 229 ++- .../src/app/reporting/requests.resolver.ts | 60 +- .../reporting/utils/build-request-report.ts | 2 + .../src/app/reporting/utils/dql-utils.ts | 118 -- clickhouse/config/config.d/config.xml | 3 + clickhouse/knexfile.ts | 23 + .../20231231065935_create_reports.ts | 40 + ...20231231100122_create_report_properties.ts | 18 + clickhouse/package-lock.json | 958 +++++++++++++ clickhouse/package.json | 17 + clickhouse/tsconfig.json | 13 + docker-compose.infra.yaml | 95 +- docker-compose.yaml | 17 +- libs/common/src/data/models.json | 26 + libs/common/src/index.ts | 5 +- libs/common/src/metrics/types.ts | 18 + libs/common/tsconfig.lib.json | 4 +- libs/types/src/index.ts | 1 + libs/types/src/reports.ts | 86 ++ libs/types/src/request.ts | 4 +- package-lock.json | 1224 ++++++++++++++++- package.json | 6 +- tsconfigz.json | 3 + 102 files changed, 3745 insertions(+), 1782 deletions(-) create mode 100644 apps/console/src/assets/providers/anthropic-logo.png delete mode 100644 apps/console/src/assets/providers/anthropic-logo.svg create mode 100644 apps/console/src/assets/providers/azure-logo.png delete mode 100644 apps/console/src/assets/providers/azure-logo.svg create mode 100644 apps/console/src/assets/providers/meta-logo.png create mode 100644 apps/console/src/assets/providers/mistral-logo.png create mode 100644 apps/console/src/assets/providers/openai-logo.png delete mode 100644 apps/console/src/assets/providers/openai-logo.svg delete mode 100644 apps/console/src/components/prompts/views/PromptMetricsView.tsx delete mode 100644 apps/console/src/lib/hooks/useGetPromptExecutionMetric.ts delete mode 100644 apps/console/src/lib/providers/MetricContext.tsx create mode 100644 apps/console/src/pages/projects/overview/charts/ModelUsageChart.tsx create mode 100644 apps/console/src/pages/requests/ModelDetails.tsx create mode 100644 apps/console/src/pages/requests/model-display-details.tsx create mode 100644 apps/server/src/app/clickhouse/clickhouse.module.ts create mode 100644 apps/server/src/app/clickhouse/clickhouse.service.ts delete mode 100644 apps/server/src/app/metrics/inputs/get-prompt-metrics.input.ts delete mode 100644 apps/server/src/app/metrics/metrics.resolver.ts delete mode 100644 apps/server/src/app/metrics/models/metric.model.ts create mode 100644 apps/server/src/app/metrics/queries/average-request-duration-query.ts create mode 100644 apps/server/src/app/metrics/queries/model-usage-query.ts create mode 100644 apps/server/src/app/metrics/queries/queries.utils.ts create mode 100644 apps/server/src/app/metrics/queries/success-error-rate-query.ts delete mode 100644 apps/server/src/app/opensearch/create-indexes.ts delete mode 100644 apps/server/src/app/opensearch/opensearch.module.ts delete mode 100644 apps/server/src/app/opensearch/opensearch.service.ts delete mode 100644 apps/server/src/app/reporting/utils/dql-utils.ts create mode 100644 clickhouse/config/config.d/config.xml create mode 100644 clickhouse/knexfile.ts create mode 100644 clickhouse/migrations/20231231065935_create_reports.ts create mode 100644 clickhouse/migrations/20231231100122_create_report_properties.ts create mode 100644 clickhouse/package-lock.json create mode 100644 clickhouse/package.json create mode 100644 clickhouse/tsconfig.json create mode 100644 libs/common/src/data/models.json create mode 100644 libs/common/src/metrics/types.ts create mode 100644 libs/types/src/reports.ts create mode 100644 tsconfigz.json diff --git a/.gitignore b/.gitignore index d0043b45..f98e4219 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,6 @@ schema.graphql temp tmp -kms/data \ No newline at end of file +kms/data + +volumes \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 0967ef42..d626db8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1,16 @@ -{} +{ + "sqltools.connections": [ + { + "server": "localhost", + "port": 8123, + "useHTTPS": false, + "database": "default", + "username": "default", + "password": "", + "tls": {}, + "previewLimit": 50, + "driver": "ClickHouse", + "name": "Local" + } + ] +} diff --git a/README.md b/README.md index c9b6fc80..02229820 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ npm install ### Spin up infrastructure dependencies via Docker Compose -Pezzo is entirely cloud-native and relies solely on open-source technologies such as [PostgreSQL](https://www.postgresql.org/), [OpenSearch](https://github.com/opensearch-project/OpenSearch), [Redis](https://github.com/redis/redis) and [Supertokens](https://supertokens.com/). +Pezzo is entirely cloud-native and relies solely on open-source technologies such as [PostgreSQL](https://www.postgresql.org/), [ClickHouse](https://github.com/ClickHouse/ClickHouse), [Redis](https://github.com/redis/redis) and [Supertokens](https://supertokens.com/). You can run these dependencies via Docker Compose: diff --git a/apps/console/project.json b/apps/console/project.json index e8af991d..ec38f1e8 100644 --- a/apps/console/project.json +++ b/apps/console/project.json @@ -112,7 +112,7 @@ "options": {}, "configurations": { "local": { - "tags": ["pezzolabs/pezzo/console"], + "tags": ["ghcr.io/pezzolabs/pezzo/console"], "push": false } } diff --git a/apps/console/src/app.tsx b/apps/console/src/app.tsx index 3dc90afd..6f5852c6 100644 --- a/apps/console/src/app.tsx +++ b/apps/console/src/app.tsx @@ -34,7 +34,6 @@ import { PromptEditView } from "./features/editor/PromptEditView"; import { EditorProvider } from "./lib/providers/EditorContext"; import { PromptTesterProvider } from "./lib/providers/PromptTesterContext"; import { PromptVersionsView } from "./components/prompts/views/PromptVersionsView"; -import { PromptMetricsView } from "./components/prompts/views/PromptMetricsView"; import { Suspense } from "react"; import { FullScreenLoader } from "./components/common/FullScreenLoader"; import { OrgPage } from "./pages/projects/OrgPage"; @@ -158,7 +157,6 @@ export function App() { } /> } /> - } /> diff --git a/apps/console/src/assets/providers/anthropic-logo.png b/apps/console/src/assets/providers/anthropic-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..60dea32b7e3bc4cbf72dfc6dcaf6dcff109cfd06 GIT binary patch literal 667 zcmV;M0%ZM(P)6p1io{0v0-(Z9en8*{gbJ(-)x}2Xurf4om7xL~4HcaUAE2Sa%7o_J zF-mTBZ|`<*NhIc8vc=rZ-oBmLH#?UC01tK;d$8k32$~`xXo`fODgGzHUhfvx>rXPq z<459CDC7m7R_hYtz|$Rz#jD2_6b2(j{1|ESez|=2EX&ebOFM$q>N#XGnZS3YQjyrB zkxOjLIsn@~23=_Fh>uEGgcu?A?2Y)~e9bl0omu3hh%lIL2Pkgx>~JfEV010BUmmM1#$HI_ux2=;G55%5_>v*A~uQz%aAIR zb}|?|NCgp9P7K8oPaM78-R3W8Tm*cjmT=Er}Cl~F%yHdbJ0OMeMGKA zCKg7_+Sqtxa=DYhx%(hNkT~$XxULIfz&U(liV^Y)3&rC1v2FW%y~$)6_|3rFGjRy# zDVxox)UC*Jf_0PsHkK(+t%E`0n9ZJ*$l-|FKuE-*c5~n}Re~&K!hDsRh3_S$1ZT6C z*y)u;Nabb;Y9JyD^ \ No newline at end of file diff --git a/apps/console/src/assets/providers/azure-logo.png b/apps/console/src/assets/providers/azure-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..66ebdbbdf959dc032f808afa1588f9735a7f1e64 GIT binary patch literal 1353 zcmV-P1-AN$P)bVMvi>qL% zlrXR+;o`Al*bwJ*PHtH!G&)PSp#hUOm#8Oo5S@x|n8?pR5-<#k2n97F;2^;Z7Qv%p z6P;KnK<3`$t!3&t4H|`HDrWC3xQf}5kt-?GW7pZm?^kQ+#6kfkZ!Qb<=&%_eRk3N= zC@roD7s~4}%uG3(7VSu@k zg+z)5gr@PqAQ`L{F_HOaLAX+>2emHU0VLAl-5eP3ulWt3ni>MsW^Sg!lvdWIfyHQJ(xioQi<~6SHe7 zK==%nY6K{#n+wZpu*=ofAR&G?&!jDcu|R-{)EaW-h70GcL1^L{O*cJe7cLAMGC8|I z-aui0n`LgU2X}KYz}xE*PV=ur-C2tfeEj|!`0}loCrF4VKN96)b{t=;BB%ufd@Mmc z>alZ3U_F9QKN!QQcO5pNlKJ#f z1wrgU!1!F+rH1h2d3AUYE15b!ZZs@hK1uNVmW)&)1`$7^;!s=IwJ^I?!^F$oodd@2Y`AdD zt#oSi4#GNl%6H%RcPnscw0Z@7riIaWM#U>Q+ioIWMq{ao2-wu7bs*rrE{@;bl#$DB z6Kl{YXJM3f+dQH14yRbCqG8palWO40lRwTEagenIOw4V{=#`~ScawBiej8?Cr_Fz) zOTavV-HjbpRcvqaCN6Nk+C*xj)*0aR+@_3Ny`}y#Sd*$aWanF+BeL7h@0L`=`qE6( zuj(JGmCgX;_qYAEw`JDw?I&mO!)IsqJSl2(iFV)ha?P_xA^sLj2F%rkVtBy#-7PnI zbvL~@6~sB7e0Lb1UyNbyt5L+F5f_m~xe-NX`>DS+6E~7sTgLY*RU5a`)o_5*_nx@H zp^~(#9!oV0F4kl6`^Qg5G57T-&JLQAFv1(z-JP6>Vg*Iieg$%m#ofG5(tf^z_n)Xn z0KDgm_$mkko}z$WV{q5LILBw6SSVnF=%^J+zP*aG;}dIBG`Cm(D})2Q@XCOvdN@si z+boO>^*y-i;r z*0cXdiJ7GmqNa|BQ})`}K(~(5{RW>y0Ui}Q{`_`WI|e+g9eKcKHAr8Go02N000000 LNkvXXu0mjfzi54J literal 0 HcmV?d00001 diff --git a/apps/console/src/assets/providers/azure-logo.svg b/apps/console/src/assets/providers/azure-logo.svg deleted file mode 100644 index 9288c792..00000000 --- a/apps/console/src/assets/providers/azure-logo.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/console/src/assets/providers/meta-logo.png b/apps/console/src/assets/providers/meta-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..90bfc0e1a41d1e2d7eb8032163e755b0f799f65b GIT binary patch literal 2527 zcmV<52_W`~P)gYf7jXA(J_dN^=A}dsZo#vVaMf zIWB9Our6_2?%IOT;w`6ZEqr(H6#g-$T~=5!l$)PQ3E1@sfp2|&7#WvEqgawSQJ1*; z=^Tg0Gn=Y-4wCl@g^ym+z@F^_YX{3AjrrQ3<_D-jrD$t946x1I+mzzubOXB{oW!v+ zWxJbv>JXNxNyrjk`dAe|zHSLprgO4JxM_bIXWq#SlcDM?23g5(OtHpPVz#cTHu2l9 zC0N(TiV*_yx`?X5b0*$0kt|Fj&|nkn_}y8&IHs$%?g}I}tXBBs2UAR*&+*rn z72d83_^PpUYl>U19>nfHDU36?a@NLAu2J~RXv*6a3!CGf11-Gx?>-~s6(bEidP5my z7m&SdO!CZx0Vo+@+jTM0Xiwn2!!!8)zJ_0k+qb9q_SM6Nm?rt(dJDHbG>IdpQt#qe zen|!AFv!9*@$jw;>xY0u6ctse9N&Bjc>V+#yWX;;j_+*g?_K}lD@mL_+&Eclb@$ zOG+v3zj+iZmLd3MX?6-BRJeT98H*3a>MrfUuPAjf->MWtO zOQj4D7CC2&<7Zm%RV}`R*={yj^Xcq==q-jK*#D9e*00Qr2oxPAd@a`g^oxH=PB}IK z-?~-c_D@%X106Y?;ptar=G0(s!BtCCz%q6|>9!C5Y1OHsv`M%6^_n!M)ga%$vV_Dh z53LsKf^*`&6Ek??63!*!v$XW^Jm!=C^cp8wyhk!`V7d+ zzIV-_Qxh=7iTvKbO)*n72jg%xGt~5WXEwJ1NUEdX6jlIRJ$xDvCOG)Ygarz6+V!bI z;ocV{&R)oj7k~WHHg>Enn{}Gfm8+`w+#TL<3?~hpGmX&gVWjGs1_zAXflFA;k%>^>>uq?S(;WYM{Fual8hMcJ7}wxE&-7Ww zyl?lW9BT%I4I&_D`J%ou?%1)^2@1S9QNo@3&vUWKEkxDAUye0wcf{}4UqlAD$&KVR z%NNzgYpi%-wAW(TZ7vgI+$wE*SZ_#;17AE)$F2<-rnrjz;Sf;EOC~9g4%YC*u0D3R zPJmVutSt_dn;)6Lb7NJP6$qQS+4v$S{zDTi{3~VW!%aNCyNz{2Wm^PXnCtpX)8jKH z|7(yr?B|YIrgb;_%|nN>qHFa|p=?!M&MnjfI~6V)$aK-`s4RYHYt&k^xc|%YX24kI5`owiBX!)eSqtNQ(}QzI3Yr>kC~aD4J{FPrD5T%F zSUR0;9(lEeXHO@XXev$%oEHZhxM_0*OPPo6xJ8V-#~^cV5hn54_&MCqGwHBdC2I$RezF5IWG*=a2(~ox6+%*wGI&+HoL4~5z;g-sJ zh^|1EssSwmC>7TD^_p**yM9o18;E>8v>MQ>5^4Luxknq1N+IBi8Nr@OTvfKGLGs75 zSOD+qdC-2UH`ykj6Qp#>D0rBzI0kcfI(2SEoy>|~SHj#rLEYee&U3aSL_is!^$+SO zb>SRqIvn?frR&HL=DN&iNAdk304Eu>^)WEzH0I4q!Q}cc^;*kOd^wP+@C$vfs1t`_ zG)H5WikCc=hJ1PO9HNF46!{(%jlW@G0GhyvK5LjP;DEy3dvJz@uufh8mgpD}7zCys zXb&^O29Aofs-4X@4r%T*WVA)L@_0@@M7&n?CY`9M`(rX>08@Z0{E)~A;eNw*2P~v{ z@*k*VkTX@G$BVjU4>-E-@DUFxfbkmgj1aBwG9$b~Vz7Rn2c`nqRKoXP&aViewX)7j zT^e(miSwX)UDVJ*>mmPnTSQt6$1k^E#L94!sqrZ)h?`^_7{*cOE_V_m4=B2=50^$CgtM1!q} zLycR|(F(M0jw9&nSMYXp4qGY$9}m$BKNs4}W0~K@BDn~w>FE=1iViya>Yw5A69|o9 z`s5Eh^4(9@drAw~ZPxv34wTmNzQw4}uLY1<9bu(OuY>e5vgpa#2)$2ge3-mNqS~bt z;o^lexFA=<+1c4(Yh!)hfb1+famdA|Jp+1W#>P-o-4ZV>SKW$hn>rHI1`gK%XsyW~ zrAti6NFoZ^K#@s-lO%}+$psa5T@{a(uR2}w2{tAPgQ~$%qQxF6&uID(w`FyjRwDi)w$19&welOnp)ohHzC4kPZDGp@UTmY!zgI~0d zI#AEG#6QzV4};9UuLCGM$gXHqNqLD`^&-id;Ho5T(^Kn6LdqFn&k#v-dWLuKjU7Mt zH0aWe8-dK_sVy1#C13aBcZTvCCa$X36X6sFuT7NWU1l-RWFn=yai|gyGXmtZllgv+MIHsk{EH?WpM>zj}$~`}lqj_W6bOlLt z&#K_QJlmDD%L5oj0oGLH?l(dA^oM9nNOPSuUf2_ZMq^Dh4DEH_pt;F(#6EstC|N#8 z=ChE*Woxe%KqUmWkuQBjzrK_b*HsR$m=oqsOEr#p@^iJ?1dW`HLC=g%_dSZjdvQ54~I; z+Q!Z27qM=kOt+g+Uny;DQkdA7A8qN0=+4bygE}yePTkwx<(3XjkJuQ0Sp&FDPJoWw zb&3J2D^&?kQT5znI};h|qX{BTPjy690WooIaM@4+q$=<0%H+wtp6mrHokcx>%2w%k z7fN}iRVc9=7U?`L9-MAH*}G3=IQkZfckEPVuf6V|B;`U4?#bqicu98uflVWfs?H#0 zoc0m&*Gl2{-Jx_q?J+)-Y#hd@E{RcBv50J{_E~(QB?{B+fz?T|V?&?@K$+RJZiU;z z=g4HZr3J>Oy{T80mKNl^@R|I4x>{E5t%2Pki9%{bvR}fQcxh7I4Y+whEr999Z(J8B z`jfp?TNqL8x%FB6FG&T9t#`b~2jU$H?=L15IcTkoZaQ7DOKn~=8D}Mu-%Luac*SA3c0000wNM^x8nf&-c#wVUj@$rZZKE5sA z0`q24#SrRUzg6n9KYQoAF1?lQmygJ+(uCB4)CxlG*uOiiV)tK;OW&QMJehbQrH}8e zs%w@u$(Gf5OP+bxo;{T8{6+S=_sskU=1D_3m4d%!>ugYhUwAYuM{iyXowwN#JW4 zNTeVyUTetZ8(VB3UQBQW%Iv`RD+-Yc;;F8`aBEIzoI#cf?q)b<0Wku0J5d2uf~R0SUW)(zON}? zTdwTxD<@?9&2#%VvcSTbqw0qtR6%^ARTyVv#&n#N<3*I$?;aRFYZrm%PIc_E299TWd5Ck6r z)F0^FFDDHNEr5RG%^RlWbgj-=f^MHwB@+`=Oisl&Qf(IpO)6Dw76y?XlTP+(dZt=| zd8h6SR$2`)*lALEPz#|xAj>k_D)5 z)%S@Ak~SXX-|_c`X@Ty&9J7Hidp4p1UAXNi7z_Ba;@wH`5b{HUigf+MSmE4WbCepD z<;DiCzcd&s`_kq@3(?nVyj9HkPSZ=ZVRTGQ1*#gmhfdg3vrFVVTkJMRK>8t{H0U^F zVlv=WPzyv4r2sWi#_eIGor+{0o`oE~>KZtdsLAKbd$IaH2(N-FJs#_`4R@rf$?{1f z=B%gtE(#yJb;+uq1Xv+oF1G7WQxbetZZ-8kReP`h(N*dF=gWPhr`*4^7Y5IWJiaum zPXPk>)Q(h;fZC%pIL()o;=MN0geesFJg57z+MqNwUClz%y2{~~!>zk72N8Lqh6F01 z!lr@V*gFLN)AJ!9Fp6u+hq?||#sFTW=%OqUxfi1h2TBw?-O=qmdIisa!Mzz!bb9V9 zj@`DN3%e2o5ql?b?+}1Ou!l`!f%;6HJq#!@l;)%y7Fhctx4Eb*L281)?kKGrDis$7 z(&_V(UPEVcnNNK${==xSs;n3V(6;n|h@#b-gg%$F{owaEYg N002ovPDHLkV1m$}Z}I>D literal 0 HcmV?d00001 diff --git a/apps/console/src/assets/providers/openai-logo.svg b/apps/console/src/assets/providers/openai-logo.svg deleted file mode 100644 index fa642606..00000000 --- a/apps/console/src/assets/providers/openai-logo.svg +++ /dev/null @@ -1 +0,0 @@ -ChatGPTChatGPT diff --git a/apps/console/src/components/metrics/StatisticBox.tsx b/apps/console/src/components/metrics/StatisticBox.tsx index 1d2783f4..e8d0e439 100644 --- a/apps/console/src/components/metrics/StatisticBox.tsx +++ b/apps/console/src/components/metrics/StatisticBox.tsx @@ -26,13 +26,14 @@ interface Props { const getProperties = ( currentValue: number, previousValue: number, - reverseColors: boolean + reverseColors: boolean, ) => { const diff = currentValue - previousValue; // Handle case where previousValue is 0 const calculatedPreviousValue = previousValue === 0 ? 1 : previousValue; const percentage = Math.abs((diff / calculatedPreviousValue) * 100); + const percentageToRender = percentage < 1 ? percentage.toFixed(3) : percentage; diff --git a/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx b/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx index ba5d0e11..f9ec0bd6 100644 --- a/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx +++ b/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx @@ -3,9 +3,9 @@ import { promptProvidersMapping } from "@pezzo/types"; import { PromptService } from "~/@generated/graphql/graphql"; // Logos -import OpenAILogo from "~/assets/providers/openai-logo.svg"; -import AzureOpenAILogo from "~/assets/providers/azure-logo.svg"; -import AnthropicLogo from "~/assets/providers/anthropic-logo.svg"; +import OpenAILogo from "~/assets/providers/openai-logo.png"; +import AzureOpenAILogo from "~/assets/providers/azure-logo.png"; +import AnthropicLogo from "~/assets/providers/anthropic-logo.png"; export const providersList: ProviderProps[] = [ { @@ -13,7 +13,7 @@ export const providersList: ProviderProps[] = [ OpenAI ), value: PromptService.OpenAiChatCompletion, @@ -24,7 +24,7 @@ export const providersList: ProviderProps[] = [ Azure OpenAI ), value: PromptService.AzureOpenAiChatCompletion, @@ -35,7 +35,7 @@ export const providersList: ProviderProps[] = [ Anthropic ), value: PromptService.AnthropicCompletion, diff --git a/apps/console/src/components/prompts/prompt-tester/PromptTesterModal.tsx b/apps/console/src/components/prompts/prompt-tester/PromptTesterModal.tsx index cd1233d4..75c71183 100644 --- a/apps/console/src/components/prompts/prompt-tester/PromptTesterModal.tsx +++ b/apps/console/src/components/prompts/prompt-tester/PromptTesterModal.tsx @@ -8,8 +8,6 @@ import { AlertTitle, Dialog, DialogContent, - DialogHeader, - DialogTitle, } from "@pezzo/ui"; import { AlertCircle } from "lucide-react"; import { cn } from "@pezzo/ui/utils"; @@ -49,18 +47,7 @@ export const PromptTesterModal = () => { {!testResult && } {testResult && (
- +
)} diff --git a/apps/console/src/components/prompts/views/PromptMetricsView.tsx b/apps/console/src/components/prompts/views/PromptMetricsView.tsx deleted file mode 100644 index 5c737206..00000000 --- a/apps/console/src/components/prompts/views/PromptMetricsView.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { - Aggregation, - PromptExecutionMetricField, -} from "~/@generated/graphql/graphql"; -import { MetricProvider } from "~/lib/providers/MetricContext"; -import { SimpleChart } from "../metrics/SimpleChart"; -import React from "react"; -import { trackEvent } from "~/lib/utils/analytics"; - -export const PromptMetricsView = () => { - React.useEffect(() => { - trackEvent("prompt_metrics_viewed"); - }, []); - - return ( -
-
- - `$${Number(v).toFixed(3)}`} - /> - -
-
- - - -
-
- - - -
-
- - `${(Number(v) / 1000).toFixed(2)}s`} - /> - -
-
- ); -}; diff --git a/apps/console/src/components/requests/RequestDetails.tsx b/apps/console/src/components/requests/RequestDetails.tsx index b8aee84d..6707ebb6 100644 --- a/apps/console/src/components/requests/RequestDetails.tsx +++ b/apps/console/src/components/requests/RequestDetails.tsx @@ -35,20 +35,16 @@ import { cn } from "@pezzo/ui/utils"; import { normalizeOpenAIChatResponse } from "~/features/chat/normalizers/openai-normalizer"; import { ChatView } from "~/features/chat/ChatView"; import { useCopyToClipboard } from "usehooks-ts"; +import { ModelDetails } from "~/pages/requests/ModelDetails"; +import { useReport } from "~/graphql/hooks/queries"; +import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; +import OpenAI from "openai"; type Mode = "chat" | "json"; interface Props { disableCopy?: boolean; id: string; - request: ObservabilityRequest; - response: ObservabilityResponse; - provider: Provider; - metadata: ObservabilityReportMetadata; - properties: ObservabilityReportProperties; - calculated: Record; - cacheEnabled: boolean; - cacheHit: boolean; } const getClientDisplayName = (client: string) => { @@ -63,11 +59,15 @@ const getClientDisplayName = (client: string) => { }; export const RequestDetails = (props: Props) => { + const { projectId } = useCurrentProject(); const disableCopy = props.disableCopy || false; - const request = props.request as ObservabilityRequest; - const response = props.response as ObservabilityResponse; - const isSuccess = response.status >= 200 && response.status < 300; - const isError = !isSuccess; + const { report } = useReport( + { projectId, reportId: props.id }, + { enabled: !!projectId && !!props.id } + ); + + const isSuccess = report.isError === false; + const isError = report.isError === true; const [selectedMode, setSelectedMode] = useState( isSuccess ? "chat" : "json" @@ -80,29 +80,23 @@ export const RequestDetails = (props: Props) => { trackEvent("prompt_test_display_mode_changed", { mode }); }; - if (props.provider !== Provider.OpenAI) { - return null; - } - const clientString = - props.metadata.client && props.metadata.clientVersion - ? `${getClientDisplayName(props.metadata.client)} - v${ - props.metadata.clientVersion - }` + report.client && report.clientVersion + ? `${getClientDisplayName(report.client)} - v${report.clientVersion}` : "Unknown"; const listData = [ { title: "Request ID", - description: props.id, + description: report.id, }, { title: "Cache", description: (
- {props.cacheEnabled ? "enabled" : "disabled"} + {report.cacheEnabled ? "enabled" : "disabled"} - {props.cacheEnabled && {props.cacheHit ? "hit" : "miss"}} + {report.cacheEnabled && {report.cacheHit ? "hit" : "miss"}}
), }, @@ -112,45 +106,46 @@ export const RequestDetails = (props: Props) => { }, { title: "Provider", - description: props.provider, + description:
{report.provider}
, }, { title: "Model", - description: request.body.model, + description: ( + + ), }, { title: "Tokens", - description: - response.status !== 200 ? ( - "0" - ) : ( -
- {props.calculated.totalTokens} - - - - - - -
-
- Completion tokens:{" "} - {response.body.usage?.completion_tokens} -
-
- Prompt tokens: - {response.body.usage?.prompt_tokens} -
+ description: report.isError ? ( + "0" + ) : ( +
+ {report.totalTokens} + + + + + + +
+
+ Prompt tokens: + {report.promptTokens}
- - - -
- ), +
+ Completion tokens:{" "} + {report.completionTokens} +
+
+ + + +
+ ), }, { title: "Cost", - description: `$${props.calculated?.totalCost?.toFixed(3) ?? 0}`, + description: `$${report?.totalCost?.toFixed(5) ?? 0}`, }, { title: "Status", @@ -167,7 +162,7 @@ export const RequestDetails = (props: Props) => { {isError ? ( <> - {response.status} Error + {report.responseStatusCode} Error ) : ( <> @@ -180,25 +175,29 @@ export const RequestDetails = (props: Props) => { }, { title: "Environment", - description: props.metadata.environment, + description: report.environment, }, { title: "Duration", - description: ms(props.calculated.duration), + description: ms(report.duration), }, ]; const renderResponse = () => { if (selectedMode === "json") { return ( - + ); } - if (props.provider === Provider.OpenAI) { - const chat = normalizeOpenAIChatResponse(request.body, response.body); - return ; - } + const chat = normalizeOpenAIChatResponse( + report.requestBody as OpenAI.ChatCompletionCreateParams, + report.responseBody as OpenAI.ChatCompletion + ); + return ; }; return ( @@ -232,7 +231,7 @@ export const RequestDetails = (props: Props) => {
- {props.metadata?.isTestPrompt && ( + {report.environment === "PLAYGROUND" && ( diff --git a/apps/console/src/components/requests/RequestResponseViewJsonView.tsx b/apps/console/src/components/requests/RequestResponseViewJsonView.tsx index 969383e8..15edbd86 100644 --- a/apps/console/src/components/requests/RequestResponseViewJsonView.tsx +++ b/apps/console/src/components/requests/RequestResponseViewJsonView.tsx @@ -1,23 +1,19 @@ -import { - ObservabilityRequest, - ObservabilityResponse, - Provider, -} from "@pezzo/types"; import { Card } from "@pezzo/ui"; +import OpenAI from "openai"; interface Props { - request: ObservabilityRequest; - response: ObservabilityResponse; + requestBody: OpenAI.ChatCompletionCreateParams; + responseBody: OpenAI.ChatCompletion; } -export const RequestResponseViewJsonView = ({ request, response }: Props) => { +export const RequestResponseViewJsonView = ({ requestBody, responseBody }: Props) => { return (

Request

-            {JSON.stringify(request.body, null, 2)}
+            {JSON.stringify(requestBody, null, 2)}
           
@@ -25,7 +21,7 @@ export const RequestResponseViewJsonView = ({ request, response }: Props) => {

Response

-            {JSON.stringify(response.body, null, 2)}
+            {JSON.stringify(responseBody, null, 2)}
           
diff --git a/apps/console/src/components/requests/filters/FilterItem.tsx b/apps/console/src/components/requests/filters/FilterItem.tsx index 731458b9..f0fddbe7 100644 --- a/apps/console/src/components/requests/filters/FilterItem.tsx +++ b/apps/console/src/components/requests/filters/FilterItem.tsx @@ -38,12 +38,8 @@ export const FilterItem = ({ onRemoveFilter, }: Props) => { const translatedField = useMemo(() => { - if (field.includes(".")) { - const fieldParts = field.split("."); - return fieldParts.reduce((acc, part) => `${acc} ${part}`, ""); - } - - return field; + const found = FILTER_FIELDS_LIST.find((f) => f.value === field); + return found?.label.toLocaleLowerCase(); }, [field]); return ( @@ -62,7 +58,7 @@ const formSchema = z.object({ field: z.string().min(1).max(100), operator: z.nativeEnum(FilterOperator), value: z.string().min(1).max(100), - property: z.string().min(1).max(100).optional(), + // property: z.string().min(1).max(100).optional(), }); export const AddFilterForm = ({ @@ -78,16 +74,13 @@ export const AddFilterForm = ({ field: "", operator: FilterOperator.Eq, value: "", - property: undefined, + // property: undefined, }, }); const onSubmit = (values: z.infer) => { const filterValue: FilterInput = { - field: - values.field !== "property" - ? values.field - : `properties.${values.property}.keyword`, + field: values.field, operator: values.operator, value: values.value, }; @@ -96,7 +89,8 @@ export const AddFilterForm = ({ form.reset(); }; - const field = form.watch("field"); + const formValues = form.watch(); + const { field } = formValues; const selectedFilterField = FILTER_FIELDS_LIST.find( (fieldInList) => fieldInList.value === field ); @@ -137,7 +131,7 @@ export const AddFilterForm = ({ )} /> - {form.watch("field") === "property" && ( + {/* {form.watch("field") === "property" && ( )} /> - )} + )} */} { const messages: ChatMessage[] = []; - console.log("request", request); - // First, populate messages from the request request.messages.forEach((message) => { if (Array.isArray(message.content)) { diff --git a/apps/console/src/graphql/definitions/queries/metrics.tsx b/apps/console/src/graphql/definitions/queries/metrics.tsx index d48fcfb3..4f920d1d 100644 --- a/apps/console/src/graphql/definitions/queries/metrics.tsx +++ b/apps/console/src/graphql/definitions/queries/metrics.tsx @@ -1,28 +1,18 @@ import { graphql } from "~/@generated/graphql"; -export const GET_PROMPT_EXECUTION_METRICS = graphql(/* GraphQL */ ` - query getMetrics($data: GetPromptMetricsInput!) { - metrics(data: $data) { - value - time +export const GET_GENERIC_PROJECT_METRIC_HISTOGRAM = graphql(/* GraphQL */ ` + query getGenericProjectMetricHistogram($data: GetProjectGenericHistogramInput!) { + genericProjectMetricHistogram(data: $data) { + data } } `); -export const GET_PROJECT_METRIC = graphql(/* GraphQL */ ` - query getProjectMetric($data: GetProjectMetricInput!) { - projectMetric(data: $data) { - currentValue - previousValue - } - } -`); - -export const GET_PROJECT_METRIC_HISTOGRAM = graphql(/* GraphQL */ ` - query getProjectMetricHistogram($data: GetProjectMetricHistogramInput!) { - projectMetricHistogram(data: $data) { - date - value - } +export const GET_PROJECT_METRIC_DELTA = graphql(/* GraphQL */ ` +query getProjectMetricDelta($data: GetProjectMetricDeltaInput!) { + projectMetricDelta(data: $data) { + currentValue, + previousValue } +} `); diff --git a/apps/console/src/graphql/definitions/queries/prompt-executions.tsx b/apps/console/src/graphql/definitions/queries/prompt-executions.tsx index 5abe3c0b..82a59c07 100644 --- a/apps/console/src/graphql/definitions/queries/prompt-executions.tsx +++ b/apps/console/src/graphql/definitions/queries/prompt-executions.tsx @@ -2,13 +2,6 @@ import { graphql } from "~/@generated/graphql"; export const TEST_PROMPT = graphql(/* GraphQL */ ` mutation testPrompt($data: TestPromptInput!) { - testPrompt(data: $data) { - reportId - calculated - properties - metadata - request - response - } + testPrompt(data: $data) } `); diff --git a/apps/console/src/graphql/definitions/queries/requests.ts b/apps/console/src/graphql/definitions/queries/requests.ts index 868566bd..d7734274 100644 --- a/apps/console/src/graphql/definitions/queries/requests.ts +++ b/apps/console/src/graphql/definitions/queries/requests.ts @@ -3,16 +3,7 @@ import { graphql } from "~/@generated/graphql"; export const GET_ALL_REQUESTS = graphql(/* GraphQL */ ` query PaginatedRequests($data: GetRequestsInput!) { paginatedRequests(data: $data) { - data { - reportId - request - response - calculated - properties - metadata - cacheEnabled - cacheHit - } + data pagination { offset limit @@ -21,3 +12,9 @@ export const GET_ALL_REQUESTS = graphql(/* GraphQL */ ` } } `); + +export const GET_REPORT = graphql(/* GraphQL */ ` + query GetReport($data: GetReportInput!) { + report(data: $data) + } +`); diff --git a/apps/console/src/graphql/hooks/queries.ts b/apps/console/src/graphql/hooks/queries.ts index 8a5979f3..b4bc1a6f 100644 --- a/apps/console/src/graphql/hooks/queries.ts +++ b/apps/console/src/graphql/hooks/queries.ts @@ -8,25 +8,26 @@ import { GET_ME } from "../definitions/queries/users"; import { GET_ALL_PROJECTS } from "../definitions/queries/projects"; import { useCurrentOrganization } from "~/lib/hooks/useCurrentOrganization"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; -import { GET_ALL_REQUESTS } from "../definitions/queries/requests"; +import { GET_ALL_REQUESTS, GET_REPORT } from "../definitions/queries/requests"; import { - GetProjectMetricHistogramQuery, - GetProjectMetricHistogramQueryVariables, - GetProjectMetricQuery, - GetProjectMetricQueryVariables, GetPromptQuery, GetPromptVersionQuery, PaginatedRequestsQuery, - RequestReport, + GetGenericProjectMetricHistogramQueryVariables, + GetGenericProjectMetricHistogramQuery, + GetProjectMetricDeltaQueryVariables, + GetProjectMetricDeltaQuery, + GetReportQuery, + GetReportQueryVariables } from "~/@generated/graphql/graphql"; -import { GraphQLErrorResponse, ReportRequestResponse } from "../types"; -import { Provider } from "@pezzo/types"; +import { GraphQLErrorResponse } from "../types"; import { GET_PROMPT, GET_PROMPT_VERSION } from "../definitions/queries/prompts"; import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; import { - GET_PROJECT_METRIC, - GET_PROJECT_METRIC_HISTOGRAM, + GET_GENERIC_PROJECT_METRIC_HISTOGRAM, + GET_PROJECT_METRIC_DELTA, } from "../definitions/queries/metrics"; +import { SerializedReport } from "@pezzo/types"; export const useProviderApiKeys = () => { const { organization } = useCurrentOrganization(); @@ -115,15 +116,6 @@ export const useGetPromptVersion = ( return { ...result, promptVersion: result.data?.promptVersion }; }; -const buildTypedRequestReportObject = (requestReport: RequestReport) => { - switch (requestReport.metadata.providers) { - case Provider.OpenAI: - return requestReport as ReportRequestResponse; - default: - return requestReport as ReportRequestResponse; - } -}; - export const useGetRequestReports = ( { offset, @@ -147,39 +139,59 @@ export const useGetRequestReports = ( }); }; -export const useProjectMetric = ( - data: GetProjectMetricQueryVariables["data"], - options: UseQueryOptions = {} +export const useReport = ( + data: GetReportQueryVariables["data"], + options: UseQueryOptions< + GetReportQuery, + GraphQLErrorResponse + > = {} +) => { + const result = useQuery({ + queryKey: ["report", data.reportId], + queryFn: () => + gqlClient.request(GET_REPORT, { + data, + }), + ...options, + }); + + return { ...result, report: result.data?.report as SerializedReport}; +}; + +export const useGenericProjectMetricHistogram = ( + data: GetGenericProjectMetricHistogramQueryVariables["data"], + options: UseQueryOptions< + GetGenericProjectMetricHistogramQuery, + GraphQLErrorResponse + > = {} ) => { - const { project } = useCurrentProject(); const result = useQuery({ - enabled: !!project, - queryKey: ["projectMetric", ...Object.values(data)], + queryKey: ["genericProjectMetricHistogram", ...Object.values(data)], queryFn: () => - gqlClient.request(GET_PROJECT_METRIC, { + gqlClient.request(GET_GENERIC_PROJECT_METRIC_HISTOGRAM, { data, }), ...options, }); - return { ...result, data: result.data?.projectMetric }; + return { ...result, histogram: result.data?.genericProjectMetricHistogram as { data: T }}; }; -export const useProjectMetricHistogram = ( - data: GetProjectMetricHistogramQueryVariables["data"], +export const useProjctMetricDelta = ( + data: GetProjectMetricDeltaQueryVariables["data"], options: UseQueryOptions< - GetProjectMetricHistogramQuery, + GetProjectMetricDeltaQuery, GraphQLErrorResponse > = {} ) => { const result = useQuery({ - queryKey: ["projectMetricHistogram", ...Object.values(data)], + queryKey: ["projectMetricDelta", ...Object.values(data)], queryFn: () => - gqlClient.request(GET_PROJECT_METRIC_HISTOGRAM, { + gqlClient.request(GET_PROJECT_METRIC_DELTA, { data, }), ...options, }); - return { ...result, histogram: result.data?.projectMetricHistogram }; + return { ...result, data: result.data?.projectMetricDelta }; }; diff --git a/apps/console/src/graphql/types.ts b/apps/console/src/graphql/types.ts index 7eec3c16..5ae78cf9 100644 --- a/apps/console/src/graphql/types.ts +++ b/apps/console/src/graphql/types.ts @@ -1,8 +1,4 @@ import { - ObservabilityReportMetadata, - ObservabilityReportProperties, - ObservabilityRequest, - ObservabilityResponse, Provider, } from "@pezzo/types"; import { GraphQLError } from "graphql-request/build/esm/types"; @@ -15,15 +11,6 @@ export interface GraphQLErrorResponse { | undefined; } -export interface ReportRequestResponse< +export type ReportRequestResponse< TProviderType extends Provider | unknown = unknown -> { - properties?: ObservabilityReportProperties; - metadata: ObservabilityReportMetadata; - request: ObservabilityRequest; - response: ObservabilityResponse; - reportId: string; - calculated: Record; - cacheEnabled?: boolean; - cacheHit?: boolean; -} +> = Record; \ No newline at end of file diff --git a/apps/console/src/lib/constants/filters.ts b/apps/console/src/lib/constants/filters.ts index 69db4eed..fb8c151c 100644 --- a/apps/console/src/lib/constants/filters.ts +++ b/apps/console/src/lib/constants/filters.ts @@ -7,40 +7,35 @@ export type FilterDefinition = { export const FILTER_FIELDS_LIST: FilterDefinition[] = [ { - value: "calculated.duration", + value: "duration", type: "number", label: "Duration (ms)", }, { - value: "metadata.environment", + value: "environment", type: "string", label: "Environment", }, { - value: "response.status", + value: "responseStatusCode", type: "number", label: "Status", }, { - value: "request.timestamp", + value: "timestamp", type: "date", label: "Timestamp", }, { - value: "calculated.totalCost", + value: "totalCost", type: "number", label: "Total Cost", }, { - value: "calculated.totalTokens", + value: "totalTokens", type: "number", label: "Total Tokens", }, - { - value: "property", - type: "string", - label: "Custom Property", - }, ]; export const NUMBER_FILTER_OPERATORS: { value: string; label: string }[] = [ @@ -80,7 +75,7 @@ export const STRING_FILTER_OPERATORS: { value: string; label: string }[] = [ label: "!=", }, { - value: "contains", + value: "like", label: "LIKE", }, ]; diff --git a/apps/console/src/lib/hooks/useFiltersAndSortParams.ts b/apps/console/src/lib/hooks/useFiltersAndSortParams.ts index 45e72316..1b718b7a 100644 --- a/apps/console/src/lib/hooks/useFiltersAndSortParams.ts +++ b/apps/console/src/lib/hooks/useFiltersAndSortParams.ts @@ -13,7 +13,7 @@ export const useFiltersAndSortParams = () => { ); useEffect(() => { - if (!sort) setSearchParams({ sort: "request.timestamp:desc" }); + if (!sort) setSearchParams({ sort: "timestamp:desc" }); }, [setSearchParams, sort]); const addFilter = useCallback( diff --git a/apps/console/src/lib/hooks/useGetPromptExecutionMetric.ts b/apps/console/src/lib/hooks/useGetPromptExecutionMetric.ts deleted file mode 100644 index c779380c..00000000 --- a/apps/console/src/lib/hooks/useGetPromptExecutionMetric.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { GET_PROMPT_EXECUTION_METRICS } from "~/graphql/definitions/queries/metrics"; -import { gqlClient } from "../graphql"; -import { GetPromptMetricsInput } from "~/@generated/graphql/graphql"; - -export const useGetPromptExecutionMetric = ( - queryKey: string[], - data: GetPromptMetricsInput -) => - useQuery({ - queryKey, - queryFn: () => - gqlClient.request(GET_PROMPT_EXECUTION_METRICS, { - data, - }), - }); diff --git a/apps/console/src/lib/providers/MetricContext.tsx b/apps/console/src/lib/providers/MetricContext.tsx deleted file mode 100644 index 88031686..00000000 --- a/apps/console/src/lib/providers/MetricContext.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { createContext, useContext, useState } from "react"; -import { useCurrentPrompt } from "./CurrentPromptContext"; -import { - Aggregation, - GetMetricsQuery, - Granularity, - PromptExecutionMetricField, -} from "~/@generated/graphql/graphql"; -import { useGetPromptExecutionMetric } from "../hooks/useGetPromptExecutionMetric"; -import { format } from "date-fns"; -import ms from "ms"; -import { trackEvent } from "../utils/analytics"; -import { - Button, - Card, - CardContent, - CardHeader, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@pezzo/ui"; -import { cn } from "@pezzo/ui/utils"; - -interface MetricContextValue { - data: GetMetricsQuery["metrics"]; - formatTimestamp: (timestamp: number) => string; -} - -export const MetricContext = createContext({ - data: undefined, - formatTimestamp: () => void 0, -}); - -export const useMetric = () => useContext(MetricContext); - -const calculateStartDate = (start: string): Date => { - const startDate = new Date(); - const subtractMs = ms(start); - startDate.setMilliseconds(startDate.getMilliseconds() + subtractMs); - return startDate; -}; - -interface Props { - children: React.ReactNode; - title: string; - field?: PromptExecutionMetricField; - aggregation: Aggregation; -} - -export const MetricProvider = ({ - children, - title, - field = null, - aggregation, -}: Props) => { - const { prompt } = useCurrentPrompt(); - const [granularity, setGranularity] = useState(Granularity.Day); - const [start, setStart] = useState("-7d"); - const startDate = calculateStartDate(start); - - const { data: metricsData, isLoading } = useGetPromptExecutionMetric( - [prompt.id, "metrics", title, granularity, start], - { - promptId: prompt.id, - field, - aggregation, - start: startDate.toISOString(), - stop: new Date().toISOString(), - granularity, - } - ); - - if (!startDate || isLoading || !metricsData) { - return null; - } - - const formatTimestamp = (timestamp) => { - const date = new Date(timestamp); - - switch (granularity) { - case Granularity.Hour: - return format(date, "yyyy-MM-dd HH:mm"); - case Granularity.Day: - return format(date, "yyyy-MM-dd"); - case Granularity.Week: - return format(date, "yyyy-MM-dd"); - case Granularity.Month: - return format(date, "MMM yyyy"); - } - }; - - const onGranularityChange = (granularity: Granularity) => { - setGranularity(granularity); - trackEvent("prompt_metric_view_changed", { - type: "granularity", - granularity, - }); - }; - - const onTimeRangeChange = (start: string) => { - setStart(start); - trackEvent("prompt_metric_view_changed", { type: "time_range", start }); - }; - - const granularities = [ - { - label: "Hour", - value: Granularity.Hour, - }, - { - label: "Day", - value: Granularity.Day, - }, - { - label: "Week", - value: Granularity.Week, - }, - { - label: "Month", - value: Granularity.Month, - }, - ]; - - return ( - - - -

{title}

-
- -
-
- -
-
- {granularities.map((item) => ( - - ))} -
-
-
- {metricsData.metrics.length === 0 ? ( -
- No data available. Try again later. -
- ) : ( -
{children}
- )} -
-
-
-
- ); -}; diff --git a/apps/console/src/lib/providers/PromptTesterContext.tsx b/apps/console/src/lib/providers/PromptTesterContext.tsx index d037014a..ded61629 100644 --- a/apps/console/src/lib/providers/PromptTesterContext.tsx +++ b/apps/console/src/lib/providers/PromptTesterContext.tsx @@ -2,11 +2,11 @@ import { createContext, useContext, useEffect, useState } from "react"; import { useTestPrompt } from "~/graphql/hooks/mutations"; import { useCurrentProject } from "../hooks/useCurrentProject"; import { useCurrentPrompt } from "./CurrentPromptContext"; -import { RequestReport } from "~/@generated/graphql/graphql"; import { EditorFormInputs, useEditorContext } from "./EditorContext"; import { UseFormReturn, useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; +import { SerializedReport } from "@pezzo/types"; const formSchema = z.record( z.string(), @@ -26,7 +26,7 @@ interface PromptTesterContextValue { runTest: () => void; isTestLoading: boolean; testError: any; - testResult: RequestReport; + testResult: SerializedReport; } const PromptTesterContext = createContext({ diff --git a/apps/console/src/pages/projects/overview/DashboardPage.tsx b/apps/console/src/pages/projects/overview/DashboardPage.tsx index 25fd6d80..d88ab04b 100644 --- a/apps/console/src/pages/projects/overview/DashboardPage.tsx +++ b/apps/console/src/pages/projects/overview/DashboardPage.tsx @@ -36,15 +36,12 @@ export const DashboardPage = () => {
- -
-
- -
+
+
- Requests/Errors (Total) + Success/Error (Total)
@@ -64,6 +61,18 @@ export const DashboardPage = () => {
+ {/*
+ +
+ Model Usage +
+
+ + + +
+
+
*/}
); diff --git a/apps/console/src/pages/projects/overview/StatisticsSection.tsx b/apps/console/src/pages/projects/overview/StatisticsSection.tsx index 0d3217ac..d98f2131 100644 --- a/apps/console/src/pages/projects/overview/StatisticsSection.tsx +++ b/apps/console/src/pages/projects/overview/StatisticsSection.tsx @@ -6,7 +6,7 @@ export const StatisticsSection = () => { const metrics = useProjectOverviewMetrics(); return ( -
+
{ currentValue={metrics?.successRate?.data?.currentValue} previousValue={metrics?.successRate?.data?.previousValue} numberSuffix="%" - numberSeparator="" + numberSeparator="." + precision={2} loading={metrics?.successRate?.isLoading} /> diff --git a/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx b/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx index 0e356d14..869fc6f5 100644 --- a/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx +++ b/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx @@ -13,20 +13,13 @@ import colors from "tailwindcss/colors"; import { useProjectMetricControls } from "./ProjectMetricContext"; import { TooltipWithTimestamp } from "./TooltipWithTimestamp"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; -import { useProjectMetricHistogram } from "~/graphql/hooks/queries"; +import { useGenericProjectMetricHistogram } from "~/graphql/hooks/queries"; import { - HistogramMetric, - ProjectMetricType, + HistogramIdType, } from "~/@generated/graphql/graphql"; import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; import { Loader2Icon } from "lucide-react"; - -const histogramToChartData = (requests: HistogramMetric[]) => { - return requests.map((entry) => ({ - timestamp: entry.date, - duration: entry.value, - })); -}; +import { MetricsTypes } from "@pezzo/common"; export const ExecutionTimeChart = () => { const { project } = useCurrentProject(); @@ -34,10 +27,10 @@ export const ExecutionTimeChart = () => { const controls = useProjectMetricControls(); const { filters } = useFiltersAndSortParams(); - const durationHistogram = useProjectMetricHistogram( + const durationHistogram = useGenericProjectMetricHistogram( { projectId: project?.id, - metric: ProjectMetricType.Duration, + histogramId: HistogramIdType.RequestDuration, bucketSize: controls.bucketSize, startDate: startDate, endDate: endDate, @@ -65,7 +58,8 @@ export const ExecutionTimeChart = () => { ); } - const data = histogramToChartData(durationHistogram.histogram); + + const data = durationHistogram.histogram.data.map((d) => ({ timestamp: d.timestamp, value: d.value })); return ( @@ -85,19 +79,19 @@ export const ExecutionTimeChart = () => { /> `${value / 1000}s`} + tickFormatter={(value) => `${(value / 1000).toFixed(2)}s`} /> - key !== "duration" ? value : `${(value as number) / 1000}s` + key !== "value" ? value : `${((value as number) / 1000).toFixed(2)}s` } /> { /> - diff --git a/apps/console/src/pages/projects/overview/charts/ModelUsageChart.tsx b/apps/console/src/pages/projects/overview/charts/ModelUsageChart.tsx new file mode 100644 index 00000000..1ae2b005 --- /dev/null +++ b/apps/console/src/pages/projects/overview/charts/ModelUsageChart.tsx @@ -0,0 +1,172 @@ +import { + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + Bar, + BarChart, +} from "recharts"; +import colors from "tailwindcss/colors"; +import { + useGenericProjectMetricHistogram, + useProjectModelUsageHistogram, +} from "~/graphql/hooks/queries"; +import { + HistogramIdType, + ModelUsageHistogramBucket, +} from "~/@generated/graphql/graphql"; +import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; +import { useTimeframeSelector } from "~/lib/providers/TimeframeSelectorContext"; +import { useProjectMetricControls } from "./ProjectMetricContext"; +import { TooltipWithTimestamp } from "./TooltipWithTimestamp"; +import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; +import { Loader2Icon } from "lucide-react"; +import { modelAuthorDetails } from "~/pages/requests/model-display-details"; +import { ModelDetails } from "~/pages/requests/ModelDetails"; +import { MetricsTypes } from "@pezzo/common"; + +interface ModelBar { + name: string; + color: string; +} + +interface ModelLegendItem { + id: string; + value: string; + color: string; + LegendItem: React.ReactNode; +} + +const histogramToChartData = ( + buckets: MetricsTypes.ModelUsageResultDataType +): { + data: any[]; + bars: Map; + legendItems: Map; +} => { + const bars = new Map(); + const legendItems = new Map(); + + const timestamps = {}; + + const data = buckets.map((bucket) => { + const { model, modelAuthor, value, timestamp } = bucket; + + if (!timestamps[timestamp]) { + timestamps[timestamp] = {}; + } + + if (!timestamps[timestamp][model]) { + timestamps[timestamp][model] = 0; + const { color } = modelAuthorDetails[modelAuthor]; + + bars.set(model, { + name: model, + color, + }); + + legendItems.set(model, { + id: model, + value: model, + color, + LegendItem: , + }); + } + + timestamps[timestamp][model] += value; + }); + + return { data, bars, legendItems }; +}; + +export const ModelUsageChart = () => { + const { startDate, endDate } = useTimeframeSelector(); + const controls = useProjectMetricControls(); + const { filters } = useFiltersAndSortParams(); + + const { project } = useCurrentProject(); + const histogram = + useGenericProjectMetricHistogram( + { + projectId: project?.id, + histogramId: HistogramIdType.ModelUsage, + bucketSize: controls.bucketSize, + startDate: startDate, + endDate: endDate, + filters, + }, + { + enabled: !!project && !!startDate && !!endDate, + } + ); + + if (histogram.isLoading) { + return ( +
+
+ +
+
+ ); + } + + const { data, bars, legendItems } = histogramToChartData( + histogram.histogram.data + ); + + return ( + + + + controls.formatTimestamp(v)} + /> + `$${Number(v).toFixed(5)}`} /> + { + return key === "Timestamp" ? value : `$${Number(value).toFixed(5)}`; + }} + /> + {/* */} + {/* + + {Array.from(legendItems.values()).sort((a, b) => a.id.localeCompare(b.id)).map( + ({ LegendItem, color }) =>
{LegendItem}
+ )} +
+ } + /> */} + + {/* {Array.from(bars).sort().map(([key, bar]) => ( + + ))} */} + + + ); +}; diff --git a/apps/console/src/pages/projects/overview/charts/ProjectMetricContext.tsx b/apps/console/src/pages/projects/overview/charts/ProjectMetricContext.tsx index 1b99539c..68803f39 100644 --- a/apps/console/src/pages/projects/overview/charts/ProjectMetricContext.tsx +++ b/apps/console/src/pages/projects/overview/charts/ProjectMetricContext.tsx @@ -31,7 +31,7 @@ const timeframeToTimestampFormatterMapping = { }; const timeframeToBucketSizeMapping = { - [Timeframe.PastHour]: ProjectMetricHistogramBucketSize.Hourly, + [Timeframe.PastHour]: ProjectMetricHistogramBucketSize.Minutely, [Timeframe.PastDay]: ProjectMetricHistogramBucketSize.Hourly, [Timeframe.PastWeek]: ProjectMetricHistogramBucketSize.Daily, [Timeframe.PastMonth]: ProjectMetricHistogramBucketSize.Daily, diff --git a/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx b/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx index e00169ba..ff6ee860 100644 --- a/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx +++ b/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx @@ -9,10 +9,8 @@ import { BarChart, } from "recharts"; import colors from "tailwindcss/colors"; -import { useProjectMetricHistogram } from "~/graphql/hooks/queries"; import { - HistogramMetric, - ProjectMetricType, + HistogramIdType, } from "~/@generated/graphql/graphql"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; import { useTimeframeSelector } from "~/lib/providers/TimeframeSelectorContext"; @@ -20,21 +18,8 @@ import { useProjectMetricControls } from "./ProjectMetricContext"; import { TooltipWithTimestamp } from "./TooltipWithTimestamp"; import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; import { Loader2Icon } from "lucide-react"; - -const histogramToChartData = ( - totalRequests: HistogramMetric[], - erroneousRequests: HistogramMetric[] -) => { - return totalRequests.map((entry) => { - const errorEntry = erroneousRequests.find((e) => e.date === entry.date); - - return { - timestamp: entry.date, - requests: entry.value, - errors: errorEntry ? errorEntry.value : 0, - }; - }); -}; +import { useGenericProjectMetricHistogram } from "~/graphql/hooks/queries"; +import { MetricsTypes } from "@pezzo/common"; export const SuccessErrorRateChart = () => { const { startDate, endDate } = useTimeframeSelector(); @@ -42,10 +27,10 @@ export const SuccessErrorRateChart = () => { const { filters } = useFiltersAndSortParams(); const { project } = useCurrentProject(); - const totalRequestsHistogram = useProjectMetricHistogram( + const histogram = useGenericProjectMetricHistogram( { projectId: project?.id, - metric: ProjectMetricType.Requests, + histogramId: HistogramIdType.SuccessErrorRate, bucketSize: controls.bucketSize, startDate: startDate, endDate: endDate, @@ -56,22 +41,8 @@ export const SuccessErrorRateChart = () => { } ); - const erroneousRequestsHistogram = useProjectMetricHistogram( - { - projectId: project?.id, - metric: ProjectMetricType.ErroneousRequests, - bucketSize: controls.bucketSize, - startDate: startDate, - endDate: endDate, - }, - { - enabled: !!project && !!startDate && !!endDate, - } - ); - if ( - totalRequestsHistogram.isLoading || - erroneousRequestsHistogram.isLoading + histogram.isLoading ) { return (
{ ); } - const data = histogramToChartData( - totalRequestsHistogram.histogram, - erroneousRequestsHistogram.histogram - ); + const data = histogram.histogram.data; return ( @@ -117,24 +85,19 @@ export const SuccessErrorRateChart = () => { - - + + ); diff --git a/apps/console/src/pages/projects/overview/charts/TooltipWithTimestamp.tsx b/apps/console/src/pages/projects/overview/charts/TooltipWithTimestamp.tsx index 0c898400..e2f55b29 100644 --- a/apps/console/src/pages/projects/overview/charts/TooltipWithTimestamp.tsx +++ b/apps/console/src/pages/projects/overview/charts/TooltipWithTimestamp.tsx @@ -3,12 +3,12 @@ import { DefaultTooltipContent } from "recharts"; export const TooltipWithTimestamp = (props) => { // payload[0] doesn't exist when tooltip isn't visible - if (props.payload[0] != null) { + if (props.payload && props.payload[0] != null) { // mutating props directly is against react's conventions // so we create a new payload with the name and value fields set to what we want const newPayload = [ { - name: "timestamp", + name: "Timestamp", // all your data which created the tooltip is located in the .payload property value: moment(props.payload[0].payload.timestamp).format( "YYYY-MM-DD HH:mm" diff --git a/apps/console/src/pages/projects/overview/useProjectOverviewMetrics.tsx b/apps/console/src/pages/projects/overview/useProjectOverviewMetrics.tsx index 44343bbb..55fd8b89 100644 --- a/apps/console/src/pages/projects/overview/useProjectOverviewMetrics.tsx +++ b/apps/console/src/pages/projects/overview/useProjectOverviewMetrics.tsx @@ -1,47 +1,31 @@ -import { ProjectMetricType } from "~/@generated/graphql/graphql"; -import { useProjectMetric } from "~/graphql/hooks/queries"; +import { DeltaMetricType } from "~/@generated/graphql/graphql"; +import { useProjctMetricDelta } from "~/graphql/hooks/queries"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; -import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; import { useTimeframeSelector } from "~/lib/providers/TimeframeSelectorContext"; export const useProjectOverviewMetrics = () => { const { project } = useCurrentProject(); const { startDate, endDate } = useTimeframeSelector(); - const { filters } = useFiltersAndSortParams(); - const useMetric = (metric: ProjectMetricType) => - useProjectMetric( + const useMetric = (metric: DeltaMetricType) => + useProjctMetricDelta( { projectId: project?.id, metric, startDate, endDate, - filters, }, { enabled: !!project && !!startDate && !!endDate, } ); - const requests = useMetric(ProjectMetricType.Requests); - const cost = useMetric(ProjectMetricType.Cost); - const avgExecutionDuration = useMetric(ProjectMetricType.Duration); - const successfulRequests = useMetric(ProjectMetricType.SuccessfulRequests); - - const successRateCurrent = - (successfulRequests?.data?.currentValue / requests?.data?.currentValue) * - 100; - const successRatePrevious = - (successfulRequests?.data?.previousValue / requests?.data?.previousValue) * - 100; - - const successRate = { - isLoading: successfulRequests.isLoading || requests.isLoading, - data: { - currentValue: successRateCurrent, - previousValue: successRatePrevious, - }, - }; + const requests = useMetric(DeltaMetricType.TotalRequests); + const cost = useMetric(DeltaMetricType.TotalCost); + const avgExecutionDuration = useMetric( + DeltaMetricType.AverageRequestDuration + ); + const successRate = useMetric(DeltaMetricType.SuccessResponses); return { requests, diff --git a/apps/console/src/pages/prompts/PromptNavigation.tsx b/apps/console/src/pages/prompts/PromptNavigation.tsx index 234f2f84..ccce311c 100644 --- a/apps/console/src/pages/prompts/PromptNavigation.tsx +++ b/apps/console/src/pages/prompts/PromptNavigation.tsx @@ -1,5 +1,5 @@ import { cn } from "@pezzo/ui/utils"; -import { BarChart4, BoxIcon, GitCommitIcon, PencilIcon } from "lucide-react"; +import { BoxIcon, GitCommitIcon, PencilIcon } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { useCurrentOrganization } from "~/lib/hooks/useCurrentOrganization"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; @@ -39,13 +39,7 @@ export const PromptNavigation = () => { icon: GitCommitIcon, href: `${basePath}/versions`, isActive: (href) => window.location.pathname === href, - }, - { - name: "Metrics", - icon: BarChart4, - href: `${basePath}/metrics`, - isActive: (href) => window.location.pathname === href, - }, + } ]; return ( diff --git a/apps/console/src/pages/requests/ModelDetails.tsx b/apps/console/src/pages/requests/ModelDetails.tsx new file mode 100644 index 00000000..92d7d2ad --- /dev/null +++ b/apps/console/src/pages/requests/ModelDetails.tsx @@ -0,0 +1,20 @@ +import { getModelDisplayDetails } from "./model-display-details"; + +interface Props { + model: string; + modelAuthor: string; +} + +export const ModelDetails = ({ model, modelAuthor }: Props) => { + const displayDetails = getModelDisplayDetails(modelAuthor); + return ( +
+ {displayDetails.name} +
{model}
+
+ ); +}; diff --git a/apps/console/src/pages/requests/RequestsPage.tsx b/apps/console/src/pages/requests/RequestsPage.tsx index 974575eb..18cc7538 100644 --- a/apps/console/src/pages/requests/RequestsPage.tsx +++ b/apps/console/src/pages/requests/RequestsPage.tsx @@ -1,12 +1,11 @@ import { useGetRequestReports } from "~/graphql/hooks/queries"; -import { useMemo } from "react"; +import { Suspense, useMemo } from "react"; import { DEFAULT_PAGE_SIZE } from "~/lib/constants/pagination"; import { RequestReportItem } from "./types"; import { usePageTitle } from "~/lib/hooks/usePageTitle"; import { RequestsTable } from "./RequestsTable"; import { ColumnDef } from "@tanstack/react-table"; import { cn } from "@pezzo/ui/utils"; -import { ReportRequestResponse } from "~/graphql/types"; import { CheckIcon, CircleSlash, MoveRightIcon } from "lucide-react"; import { RequestItemTags } from "./RequestTags"; import { RequestDetails } from "~/components/requests"; @@ -21,6 +20,8 @@ import { AccordionTrigger, Card, } from "@pezzo/ui"; +import { ModelDetails } from "./ModelDetails"; +import { SerializedPaginatedReport } from "@pezzo/types"; const getTableColumns = (): ColumnDef[] => { const columns: ColumnDef[] = [ @@ -67,7 +68,19 @@ const getTableColumns = (): ColumnDef[] => { accessorKey: "model", id: "model", header: "Model", - cell: ({ row }) =>
{row.original.model}
, + cell: ({ row }) => { + const { model, modelAuthor } = row.original; + return ; + }, + enableSorting: true, + }, + { + accessorKey: "provider", + id: "provider", + header: "Provider", + cell: ({ row }) => ( +
{row.original.provider}
+ ), enableSorting: true, }, { @@ -88,7 +101,7 @@ const getTableColumns = (): ColumnDef[] => { accessorKey: "cost", id: "cost", header: "Cost", - cell: ({ row }) =>
${row.original.cost.toFixed(3)}
, + cell: ({ row }) =>
${row.original.cost.toFixed(5)}
, }, { id: "tags", @@ -143,60 +156,46 @@ export const RequestsPage = () => { const data: RequestReportItem[] = useMemo( () => - paginatedResults.map((report: ReportRequestResponse) => { + paginatedResults.map((report: SerializedPaginatedReport) => { return { - reportId: report.reportId, - timestamp: report.request.timestamp, - status: report.response.status, - duration: report.calculated.duration ?? 0, - totalTokens: report.calculated.totalTokens ?? 0, - cost: report.calculated.totalCost ?? 0, - isTestPrompt: (report.metadata?.isTestPrompt as boolean) || false, - promptId: report.metadata?.promptId ?? null, - cacheEnabled: report.cacheEnabled ?? false, - cacheHit: report.cacheHit ?? false, - model: report.response.body.model as string, + reportId: report.id, + timestamp: report.timestamp, + status: report.responseStatusCode, + duration: report.duration ?? 0, + totalTokens: report.totalTokens ?? 0, + cost: report.totalCost ?? 0, + isTestPrompt: report.environment === "PLAYGROUND" || false, + promptId: null, + cacheEnabled: report.cacheEnabled, + cacheHit: report.cacheHit, + model: report.model, + modelAuthor: report.modelAuthor, + provider: report.provider, }; }), [paginatedResults] ); - const currentReport = useMemo( - () => - requests?.paginatedRequests.data.find( - (r) => r.reportId === inspectedRequestId - ), - [requests?.paginatedRequests.data, inspectedRequestId] - ); - return ( <> setInspectedRequestId(undefined)} - open={!!currentReport} + open={!!inspectedRequestId} > - {currentReport != null && ( - + {inspectedRequestId && ( + + + )} -
-
+
+

Requests

-
+
diff --git a/apps/console/src/pages/requests/model-display-details.tsx b/apps/console/src/pages/requests/model-display-details.tsx new file mode 100644 index 00000000..2a811b0a --- /dev/null +++ b/apps/console/src/pages/requests/model-display-details.tsx @@ -0,0 +1,34 @@ +import OpenAILogo from "~/assets/providers/openai-logo.png"; +import MistralLogo from "~/assets/providers/mistral-logo.png"; +import MetaLogo from "~/assets/providers/meta-logo.png"; + +export const modelAuthorDetails = { + openai: { + image: OpenAILogo, + name: "OpenAI", + color: "#3B976B" + }, + mistral: { + image: MistralLogo, + name: "Mistral", + color: "#cf651f" + }, + meta: { + image: MetaLogo, + name: "Meta", + color: "#579BE0" + }, +}; + +export const getModelDisplayDetails = (modelAuthor: string) => { + const authorDetails = modelAuthorDetails[modelAuthor]; + + if (!authorDetails) { + return { + image: , + name: "Unknown" + } + } + + return authorDetails; +} \ No newline at end of file diff --git a/apps/console/src/pages/requests/types.ts b/apps/console/src/pages/requests/types.ts index b8ae777e..be49f76f 100644 --- a/apps/console/src/pages/requests/types.ts +++ b/apps/console/src/pages/requests/types.ts @@ -12,4 +12,6 @@ export interface RequestReportItem { cacheEnabled: boolean; cacheHit: boolean; model: string; + modelAuthor: string; + provider: string; } diff --git a/apps/console/tsconfig.app.json b/apps/console/tsconfig.app.json index e802584e..8932816f 100644 --- a/apps/console/tsconfig.app.json +++ b/apps/console/tsconfig.app.json @@ -23,5 +23,5 @@ "**/*.stories.jsx", "**/*.stories.tsx" ], - "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] + "include": ["src/main.tsx"] } diff --git a/apps/proxy/project.json b/apps/proxy/project.json index 6db888c6..3a0cce2b 100644 --- a/apps/proxy/project.json +++ b/apps/proxy/project.json @@ -81,7 +81,7 @@ "options": {}, "configurations": { "local": { - "tags": ["pezzolabs/pezzo/proxy"], + "tags": ["ghcr.io/pezzolabs/pezzo/proxy"], "push": false } } diff --git a/apps/proxy/src/lib/OpenAIHandler.ts b/apps/proxy/src/lib/OpenAIHandler.ts index 78754e58..ac3a14be 100644 --- a/apps/proxy/src/lib/OpenAIHandler.ts +++ b/apps/proxy/src/lib/OpenAIHandler.ts @@ -63,7 +63,7 @@ export class OpenAIV1Handler { ); } - const baseMetadata: ObservabilityReportMetadata = { + const baseMetadata: any = { environment: pezzo.options.environment, provider: Provider.OpenAI, type: PromptExecutionType.ChatCompletion, diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 67412168..698d134f 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -14,6 +14,10 @@ COPY ./dist/apps/server . RUN npx prisma generate +COPY ./clickhouse ./clickhouse + +RUN cd clickhouse && npm i + ENV PORT=3000 EXPOSE $PORT diff --git a/apps/server/project.json b/apps/server/project.json index 6f789fee..f4bc16e8 100644 --- a/apps/server/project.json +++ b/apps/server/project.json @@ -105,7 +105,7 @@ "options": {}, "configurations": { "local": { - "tags": ["pezzolabs/pezzo/server"], + "tags": ["ghcr.io/pezzolabs/pezzo/server"], "push": false } } diff --git a/apps/server/scripts/generate-graphql-schema.ts b/apps/server/scripts/generate-graphql-schema.ts index c810409c..471f7a0e 100644 --- a/apps/server/scripts/generate-graphql-schema.ts +++ b/apps/server/scripts/generate-graphql-schema.ts @@ -5,7 +5,7 @@ import { PrismaClient } from "@prisma/client"; import supertokens from "supertokens-node"; import { AppModule } from "../src/app/app.module"; import { KafkaConsumerService, KafkaProducerService } from "@pezzo/kafka"; -import { OpenSearchService } from "../src/app/opensearch/opensearch.service"; +import { ClickHouseService } from "../src/app/clickhouse/clickhouse.service"; import { RedisService } from "../src/app/redis/redis.service"; // This script only runs in GitHub Actions @@ -29,7 +29,7 @@ export default async function generateGraphQLSchema(): Promise { // eslint-disable-next-line @typescript-eslint/no-empty-function KafkaProducerService.prototype.connect = async () => {}; // eslint-disable-next-line @typescript-eslint/no-empty-function - OpenSearchService.prototype.onModuleInit = async () => {}; + ClickHouseService.prototype.onModuleInit = async () => {}; // eslint-disable-next-line @typescript-eslint/no-empty-function RedisService.prototype.onModuleInit = async () => {}; diff --git a/apps/server/src/app/app.module.ts b/apps/server/src/app/app.module.ts index 4867bc0a..66df74e4 100644 --- a/apps/server/src/app/app.module.ts +++ b/apps/server/src/app/app.module.ts @@ -18,7 +18,6 @@ import { MetricsModule } from "./metrics/metrics.module"; import { LoggerModule } from "./logger/logger.module"; import { AnalyticsModule } from "./analytics/analytics.module"; import { ReportingModule } from "./reporting/reporting.module"; -import { OpenSearchModule } from "./opensearch/opensearch.module"; import { NotificationsModule } from "./notifications/notifications.module"; import { getConfigSchema } from "./config/common-config-schema"; import { PromptTesterModule } from "./prompt-tester/prompt-tester.module"; @@ -26,6 +25,7 @@ import { ClsModule } from "./cls.module"; import { CacheModule } from "./cache/cache.module"; import { RedisModule } from "./redis/redis.module"; import { EncryptionModule } from "./encryption/encryption.module"; +import { ClickhHouseModule } from "./clickhouse/clickhouse.module"; const isCloud = process.env.PEZZO_CLOUD === "true"; @@ -41,7 +41,7 @@ const isCloud = process.env.PEZZO_CLOUD === "true"; validate: process.env.SKIP_CONFIG_VALIDATION === "true" ? () => ({}) : undefined, }), - OpenSearchModule, + ClickhHouseModule, RedisModule, EncryptionModule, EventEmitterModule.forRoot(), diff --git a/apps/server/src/app/clickhouse/clickhouse.module.ts b/apps/server/src/app/clickhouse/clickhouse.module.ts new file mode 100644 index 00000000..3c75c2fe --- /dev/null +++ b/apps/server/src/app/clickhouse/clickhouse.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { LoggerModule } from "../logger/logger.module"; +import { ClickHouseService } from "./clickhouse.service"; + +@Module({ + imports: [LoggerModule], + providers: [ClickHouseService], + exports: [ClickHouseService], +}) +export class ClickhHouseModule {} diff --git a/apps/server/src/app/clickhouse/clickhouse.service.ts b/apps/server/src/app/clickhouse/clickhouse.service.ts new file mode 100644 index 00000000..906df498 --- /dev/null +++ b/apps/server/src/app/clickhouse/clickhouse.service.ts @@ -0,0 +1,66 @@ +import { ConfigService } from "@nestjs/config"; +import { Injectable, OnModuleInit } from "@nestjs/common"; +import { createLogger } from "../logger/create-logger"; +import { pino } from "pino"; +import { createClient, ClickHouseClient } from "@clickhouse/client"; // or '@clickhouse/client-web' +import { knex, Knex } from "knex"; + +@Injectable() +export class ClickHouseService implements OnModuleInit { + public client: ClickHouseClient; + public knex: Knex; + private logger: pino.Logger; + public requestsIndexAlias: string; + + constructor(private config: ConfigService) { + this.logger = createLogger({ + scope: "ClickHouseService", + }); + } + + async onModuleInit() { + const host = this.config.get("CLICKHOUSE_HOST"); + const port = this.config.get("CLICKHOUSE_PORT"); + const username = this.config.get("CLICKHOUSE_USER"); + const password = this.config.get("CLICKHOUSE_PASSWORD"); + const protocol = this.config.get("CLICKHOUSE_PROTOCOL"); + const database = "default"; + + this.logger.info("Creating ClickHouse client"); + + this.client = createClient({ + host: `${protocol}://${host}:${port}`, + username, + password, + database, + }); + + this.logger.info("Creating Knex instance"); + + this.knex = knex({ + client: "mysql2", + connection: { + host, + port: 9004, + user: username, + password, + database, + timezone: "Z" + }, + }); + + await this.healthCheck(); + } + + async healthCheck() { + try { + await this.client.query({ + query: "SELECT 1", + format: "JSONEachRow", + }); + } catch (error) { + this.logger.error({ error }, "ClickHouse healthcheck failed"); + throw error; + } + } +} diff --git a/apps/server/src/app/common/filters/filter.input.ts b/apps/server/src/app/common/filters/filter.input.ts index d8cba030..c01fd769 100644 --- a/apps/server/src/app/common/filters/filter.input.ts +++ b/apps/server/src/app/common/filters/filter.input.ts @@ -6,7 +6,7 @@ export enum FilterOperator { neq = "neq", in = "in", nin = "nin", - contains = "contains", + like = "like", gt = "gt", gte = "gte", lt = "lt", @@ -16,6 +16,31 @@ registerEnumType(FilterOperator, { name: "FilterOperator", }); +export const getSQLOperatorByFilterOperator = (operator: FilterOperator): string => { + switch (operator) { + case FilterOperator.eq: + return "="; + case FilterOperator.neq: + return "!="; + case FilterOperator.in: + return "IN"; + case FilterOperator.nin: + return "NOT IN"; + case FilterOperator.like: + return "LIKE"; + case FilterOperator.gt: + return ">"; + case FilterOperator.gte: + return ">="; + case FilterOperator.lt: + return "<"; + case FilterOperator.lte: + return "<="; + default: + throw new Error(`Unknown filter operator: ${operator}`); + } +} + @InputType() export class FilterInput { @Field(() => String, { nullable: false }) diff --git a/apps/server/src/app/common/filters/shared.ts b/apps/server/src/app/common/filters/shared.ts index eec97477..9ef27e55 100644 --- a/apps/server/src/app/common/filters/shared.ts +++ b/apps/server/src/app/common/filters/shared.ts @@ -1,10 +1,10 @@ -import { RequestReport } from "../../reporting/object-types/request-report.model"; +import { ReportSchema } from "@pezzo/types"; -export type FilterFields = - RequestReport[TMainKey] extends Record - ? keyof RequestReport[TMainKey] extends string - ? `${TMainKey}.${keyof RequestReport[TMainKey]}` +export type FilterFields = + ReportSchema[TMainKey] extends Record + ? keyof ReportSchema[TMainKey] extends string + ? `${TMainKey}.${keyof ReportSchema[TMainKey]}` : `${TMainKey}` : `${TMainKey}`; -export type RequestReportFilterFields = FilterFields; +export type RequestReportFilterFields = FilterFields; diff --git a/apps/server/src/app/config/common-config-schema.ts b/apps/server/src/app/config/common-config-schema.ts index 3b467564..e223e179 100644 --- a/apps/server/src/app/config/common-config-schema.ts +++ b/apps/server/src/app/config/common-config-schema.ts @@ -9,9 +9,11 @@ const commonConfigSchema = { SUPERTOKENS_API_KEY: Joi.string().optional(), SUPERTOKENS_API_DOMAIN: Joi.string().default("http://localhost:3000"), SUPERTOKENS_WEBSITE_DOMAIN: Joi.string().default("http://localhost:4200"), - OPENSEARCH_URL: Joi.string().required(), - OPENSEARCH_AUTH: Joi.string().valid("insecure", "aws").default("insecure"), - OPENSEARCH_INDEX_REQUESTS_ALIAS: Joi.string().default("requests-alias"), + CLICKHOUSE_HOST: Joi.string().default("localhost"), + CLICKHOUSE_PORT: Joi.string().default("8123"), + CLICKHOUSE_USER: Joi.string().default("default"), + CLICKHOUSE_PASSWORD: Joi.string().default("default"), + CLICKHOUSE_PROTOCOL: Joi.string().default("http"), REDIS_URL: Joi.string().required(), REDIS_TLS_ENABLED: Joi.boolean().default(false), KMS_REGION: Joi.string().default("us-east-1"), diff --git a/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts b/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts index 75e38fe3..815a3e98 100644 --- a/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts +++ b/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts @@ -7,12 +7,48 @@ export enum ProjectMetricType { duration = "duration", successfulRequests = "successfulRequests", erroneousRequests = "erroneousRequests", + model = "model" } registerEnumType(ProjectMetricType, { name: "ProjectMetricType", }); +export enum HistogramIdType { + requestDuration = "requestDuration", + successErrorRate = "successErrorRate", + modelUsage = "modelUsage", +} + +registerEnumType(HistogramIdType, { + name: "HistogramIdType", +}); + +export enum DeltaAggregation { + sum = "sum", + avg = "avg", + min = "min", + max = "max", + count = "count", +} + +registerEnumType(DeltaAggregation, { + name: "DeltaAggregation", +}); + +export enum DeltaMetricType { + TotalCost = "TotalCost", + TotalTokens = "TotalTokens", + TotalRequests = "TotalRequests", + AverageRequestDuration = "AverageRequestDuration", + SuccessResponses = "SuccessfulResponses", + ErrorResponses = "ErroneousResponses", +} + +registerEnumType(DeltaMetricType, { + name: "DeltaMetricType", +}); + @InputType() export class GetProjectMetricInput { @Field(() => String, { nullable: false }) @@ -32,6 +68,7 @@ export class GetProjectMetricInput { } export enum ProjectMetricHistogramBucketSize { + minutely = "1m", hourly = "1h", daily = "1d", weekly = "1w", @@ -44,13 +81,10 @@ registerEnumType(ProjectMetricHistogramBucketSize, { }); @InputType() -export class GetProjectMetricHistogramInput { +export class BaseProjectMetricInput { @Field(() => String, { nullable: false }) projectId: string; - @Field(() => ProjectMetricType, { nullable: false }) - metric: ProjectMetricType; - @Field(() => Date, { nullable: false }) startDate: Date; @@ -63,3 +97,35 @@ export class GetProjectMetricHistogramInput { @Field(() => [FilterInput], { nullable: true }) filters?: FilterInput[]; } + +@InputType() +export class GetProjectMetricHistogramInput extends BaseProjectMetricInput { + @Field(() => ProjectMetricType, { nullable: false }) + metric: ProjectMetricType; +} + + +@InputType() +export class GetProjectGenericHistogramInput extends BaseProjectMetricInput { + @Field(() => HistogramIdType, { nullable: false }) + histogramId: HistogramIdType; +} + +@InputType() +export class GetProjectModelUsageHistogramInput extends BaseProjectMetricInput { +} + +@InputType() +export class GetProjectMetricDeltaInput { + @Field(() => String, { nullable: false }) + projectId: string; + + @Field(() => Date, { nullable: false }) + startDate: Date; + + @Field(() => Date, { nullable: false }) + endDate: Date; + + @Field(() => DeltaMetricType, { nullable: true }) + metric: DeltaMetricType; +} \ No newline at end of file diff --git a/apps/server/src/app/metrics/inputs/get-prompt-metrics.input.ts b/apps/server/src/app/metrics/inputs/get-prompt-metrics.input.ts deleted file mode 100644 index 7f51f50d..00000000 --- a/apps/server/src/app/metrics/inputs/get-prompt-metrics.input.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Field, InputType, registerEnumType } from "@nestjs/graphql"; -import { FilterInput } from "../../common/filters/filter.input"; - -export enum PromptExecutionMetricField { - totalCost = "totalCost", - totalTokens = "totalTokens", - duration = "duration", -} - -registerEnumType(PromptExecutionMetricField, { - name: "PromptExecutionMetricField", -}); - -export enum Aggregation { - sum = "sum", - avg = "avg", - min = "min", - max = "max", - count = "count", -} - -registerEnumType(Aggregation, { - name: "Aggregation", -}); - -export enum Granularity { - hour = "hour", - day = "day", - week = "week", - month = "month", -} - -registerEnumType(Granularity, { - name: "Granularity", -}); - -@InputType() -export class GetPromptMetricsInput { - @Field(() => String, { nullable: false }) - promptId: string; - - @Field(() => PromptExecutionMetricField, { nullable: true }) - field?: PromptExecutionMetricField; - - @Field(() => String, { nullable: false }) - start: string; - - @Field(() => String, { nullable: true, defaultValue: "now()" }) - stop?: string = "now()"; - - @Field(() => Aggregation, { nullable: false }) - aggregation: Aggregation; - - @Field(() => Granularity, { nullable: false }) - granularity: Granularity; - - @Field(() => String, { nullable: true }) - fillEmpty?: string = null; - - @Field(() => [FilterInput], { nullable: true }) - filters: FilterInput[]; -} diff --git a/apps/server/src/app/metrics/metrics.module.ts b/apps/server/src/app/metrics/metrics.module.ts index ecae3faa..6d322e64 100644 --- a/apps/server/src/app/metrics/metrics.module.ts +++ b/apps/server/src/app/metrics/metrics.module.ts @@ -1,16 +1,14 @@ import { Module } from "@nestjs/common"; -import { MetricsResolver } from "./metrics.resolver"; import { IdentityModule } from "../identity/identity.module"; import { PromptsModule } from "../prompts/prompts.module"; import { PrismaService } from "../prisma.service"; -import { OpenSearchModule } from "../opensearch/opensearch.module"; import { ProjectMetricsResolver } from "./project-metrics.resolver"; import { ProjectMetricsService } from "./project-metrics.service"; +import { ClickhHouseModule } from "../clickhouse/clickhouse.module"; @Module({ - imports: [IdentityModule, PromptsModule, OpenSearchModule], + imports: [IdentityModule, PromptsModule, ClickhHouseModule], providers: [ - MetricsResolver, ProjectMetricsResolver, PrismaService, ProjectMetricsService, diff --git a/apps/server/src/app/metrics/metrics.resolver.ts b/apps/server/src/app/metrics/metrics.resolver.ts deleted file mode 100644 index 36b44bfa..00000000 --- a/apps/server/src/app/metrics/metrics.resolver.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { Args, Query, Resolver } from "@nestjs/graphql"; -import { Metric } from "./models/metric.model"; -import { - GetPromptMetricsInput, - Granularity, -} from "./inputs/get-prompt-metrics.input"; -import { AuthGuard } from "../auth/auth.guard"; -import { - InternalServerErrorException, - NotFoundException, - UseGuards, -} from "@nestjs/common"; -import { PinoLogger } from "../logger/pino-logger"; -import { CurrentUser } from "../identity/current-user.decorator"; -import { RequestUser } from "../identity/users.types"; -import { isOrgMemberOrThrow } from "../identity/identity.utils"; -import { PromptsService } from "../prompts/prompts.service"; -import { Prompt } from "@prisma/client"; -import { PrismaService } from "../prisma.service"; -import { OpenSearchService } from "../opensearch/opensearch.service"; -import { ApiResponse } from "@opensearch-project/opensearch/."; -import { - MsearchBody, - SearchResponse, -} from "@opensearch-project/opensearch/api/types"; - -const granularityMapping = { - [Granularity.hour]: "1h", - [Granularity.day]: "1d", - [Granularity.week]: "1w", - [Granularity.month]: "1M", -}; - -@UseGuards(AuthGuard) -@Resolver(() => Metric) -export class MetricsResolver { - constructor( - private prismaService: PrismaService, - private openSearchService: OpenSearchService, - private promptService: PromptsService, - private readonly logger: PinoLogger - ) {} - - @Query(() => [Metric]) - async metrics( - @Args("data") data: GetPromptMetricsInput, - @CurrentUser() user: RequestUser - ) { - this.logger.assign({ data }); - this.logger.info("Getting metrics"); - - let prompt: Prompt; - - try { - prompt = await this.promptService.getPrompt(data.promptId); - } catch (error) { - this.logger.error({ error }, "Error getting prompt"); - throw new InternalServerErrorException(); - } - - if (!prompt) { - throw new NotFoundException(); - } - - const project = await this.prismaService.project.findUnique({ - where: { - id: prompt.projectId, - }, - }); - - isOrgMemberOrThrow(user, project.organizationId); - - const { start, stop, field, aggregation, granularity, promptId } = data; - - const startDate = new Date(); - startDate.setDate(startDate.getDate() - 7); - - let aggs: any = {}; - - if (aggregation !== "count") { - aggs = { - field_aggregation: { - [aggregation]: { field: `calculated.${field}` }, - }, - }; - } else { - aggs = { - field_aggregation: { - value_count: { - field: "_index", - }, - }, - }; - } - - const body: MsearchBody = { - query: { - bool: { - filter: [ - { - term: { "metadata.promptId": promptId }, - }, - { - range: { - "request.timestamp": { - gte: start, - lte: stop, - }, - }, - }, - ], - }, - }, - size: 0, - aggs: { - time_buckets: { - date_histogram: { - field: "request.timestamp", - interval: granularityMapping[granularity], - extended_bounds: { - min: start, - max: stop, - }, - }, - aggs, - }, - }, - }; - - const query = { - index: this.openSearchService.requestsIndexAlias, - body, - }; - - const osResponse: ApiResponse> = - await this.openSearchService.client.search(query); - - const { aggregations } = osResponse.body; - const metrics = aggregations.time_buckets["buckets"].map((bucket) => ({ - value: bucket.field_aggregation.value || 0, - time: new Date(bucket.key_as_string), - })); - - return metrics; - } -} diff --git a/apps/server/src/app/metrics/models/metric.model.ts b/apps/server/src/app/metrics/models/metric.model.ts deleted file mode 100644 index 1a44c6ea..00000000 --- a/apps/server/src/app/metrics/models/metric.model.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Field } from "@nestjs/graphql"; -import { ObjectType } from "@nestjs/graphql"; -import GraphQLJSON from "graphql-type-json"; - -@ObjectType() -export class Metric { - @Field(() => Date, { nullable: false }) - time: Date; - - @Field(() => Number, { nullable: false }) - value: number; - - @Field(() => GraphQLJSON, { nullable: true, defaultValue: {} }) - metadata = {}; -} diff --git a/apps/server/src/app/metrics/models/project-metric.model.ts b/apps/server/src/app/metrics/models/project-metric.model.ts index 628fba04..dc888d1b 100644 --- a/apps/server/src/app/metrics/models/project-metric.model.ts +++ b/apps/server/src/app/metrics/models/project-metric.model.ts @@ -1,5 +1,6 @@ import { Field } from "@nestjs/graphql"; import { ObjectType } from "@nestjs/graphql"; +import GraphQLJSON from "graphql-type-json"; @ObjectType() export class ProjectMetric { @@ -13,8 +14,44 @@ export class ProjectMetric { @ObjectType() export class HistogramMetric { @Field(() => String, { nullable: false }) - date: string; // ISO date string for the bucket + timestamp: string; // ISO date string for the bucket @Field(() => Number, { nullable: false }) value: number; // Value for the metric at that date } + +@ObjectType() +export class ModelUsageHistogramValue { + @Field(() => String, { nullable: false }) + model: string; + + @Field(() => String, { nullable: false }) + modelAuthor: string; + + @Field(() => Number, { nullable: false }) + value: number; +} + +@ObjectType() +export class ModelUsageHistogramBucket { + @Field(() => String, { nullable: false }) + timestamp: string; + + @Field(() => [ModelUsageHistogramValue], { nullable: false }) + values: ModelUsageHistogramValue[]; // Value for the metric at that date +} + +@ObjectType() +export class GenericProjectHistogramResult { + @Field(() => [GraphQLJSON], { nullable: false }) + data: any; +} + +@ObjectType() +export class ProjectMetricDeltaResult { + @Field(() => Number, { nullable: false }) + currentValue: number; + + @Field(() => Number, { nullable: false }) + previousValue: number; +} \ No newline at end of file diff --git a/apps/server/src/app/metrics/project-metrics.resolver.ts b/apps/server/src/app/metrics/project-metrics.resolver.ts index 5a8f388e..63b81265 100644 --- a/apps/server/src/app/metrics/project-metrics.resolver.ts +++ b/apps/server/src/app/metrics/project-metrics.resolver.ts @@ -6,11 +6,14 @@ import { CurrentUser } from "../identity/current-user.decorator"; import { RequestUser } from "../identity/users.types"; import { isOrgMemberOrThrow } from "../identity/identity.utils"; import { PrismaService } from "../prisma.service"; -import { HistogramMetric, ProjectMetric } from "./models/project-metric.model"; import { - GetProjectMetricHistogramInput, - GetProjectMetricInput, - ProjectMetricType, + GenericProjectHistogramResult, + ProjectMetric, + ProjectMetricDeltaResult, +} from "./models/project-metric.model"; +import { + GetProjectGenericHistogramInput, + GetProjectMetricDeltaInput, } from "./inputs/get-project-metrics.input"; import { ProjectMetricsService } from "./project-metrics.service"; @@ -23,15 +26,15 @@ export class ProjectMetricsResolver { private readonly logger: PinoLogger ) {} - @Query(() => ProjectMetric) - async projectMetric( - @Args("data") data: GetProjectMetricInput, + @Query(() => GenericProjectHistogramResult) + async genericProjectMetricHistogram( + @Args("data") data: GetProjectGenericHistogramInput, @CurrentUser() user: RequestUser - ): Promise { + ): Promise { this.logger.assign({ data }); - this.logger.info("Getting project metric"); + this.logger.info("Getting project metric histogram"); - const { projectId, metric, startDate, endDate, filters } = data; + const { projectId, histogramId, startDate, endDate, bucketSize, filters } = data; const project = await this.prismaService.project.findUnique({ where: { @@ -40,55 +43,25 @@ export class ProjectMetricsResolver { }); isOrgMemberOrThrow(user, project.organizationId); - - switch (metric) { - case ProjectMetricType.requests: - return this.projectMetricsService.getRequests( - projectId, - startDate, - endDate, - filters - ); - case ProjectMetricType.cost: - return this.projectMetricsService.getCost( - projectId, - startDate, - endDate, - filters - ); - case ProjectMetricType.duration: - return this.projectMetricsService.getAvgDuration( - projectId, - startDate, - endDate, - filters - ); - case ProjectMetricType.successfulRequests: - return this.projectMetricsService.getSuccessfulRequests( - projectId, - startDate, - endDate, - filters - ); - case ProjectMetricType.erroneousRequests: - return this.projectMetricsService.getErroneousRequests( - projectId, - startDate, - endDate, - filters - ); - } + return this.projectMetricsService.getGenericHistogram( + projectId, + histogramId, + startDate, + endDate, + bucketSize, + filters + ); } - @Query(() => [HistogramMetric]) - async projectMetricHistogram( - @Args("data") data: GetProjectMetricHistogramInput, + @Query(() => ProjectMetricDeltaResult) + async projectMetricDelta( + @Args("data") data: GetProjectMetricDeltaInput, @CurrentUser() user: RequestUser - ): Promise { + ): Promise { this.logger.assign({ data }); - this.logger.info("Getting project metric histogram"); + this.logger.info("Getting project metric with deltas"); - const { projectId, metric, startDate, endDate, bucketSize, filters } = data; + const { projectId, startDate, endDate, metric } = data; const project = await this.prismaService.project.findUnique({ where: { @@ -97,13 +70,14 @@ export class ProjectMetricsResolver { }); isOrgMemberOrThrow(user, project.organizationId); - return this.projectMetricsService.getHistogram( + + const result = await this.projectMetricsService.getProjectMetricDelta( projectId, - metric, startDate, endDate, - bucketSize, - filters + metric, ); + + return result; } } diff --git a/apps/server/src/app/metrics/project-metrics.service.ts b/apps/server/src/app/metrics/project-metrics.service.ts index 80a38714..fd3fd94b 100644 --- a/apps/server/src/app/metrics/project-metrics.service.ts +++ b/apps/server/src/app/metrics/project-metrics.service.ts @@ -1,256 +1,128 @@ -import { Injectable } from "@nestjs/common"; -import { ProjectMetricType } from "./inputs/get-project-metrics.input"; -import { OpenSearchService } from "../opensearch/opensearch.service"; -import { HistogramMetric, ProjectMetric } from "./models/project-metric.model"; +import { BadRequestException, Injectable, InternalServerErrorException } from "@nestjs/common"; import { - buildBaseProjectMetricQuery, - getStartAndEndDates, -} from "./metrics.utils"; + DeltaMetricType, + HistogramIdType, + ProjectMetricHistogramBucketSize, +} from "./inputs/get-project-metrics.input"; +import { + GenericProjectHistogramResult, + ProjectMetricDeltaResult, +} from "./models/project-metric.model"; import { FilterInput } from "../common/filters/filter.input"; -import { mapFiltersToDql } from "../reporting/utils/dql-utils"; +import { ClickHouseService } from "../clickhouse/clickhouse.service"; +import { averageRequestDurationQuery } from "./queries/average-request-duration-query"; +import { Knex } from "knex"; +import { successErrorRateQuery } from "./queries/success-error-rate-query"; @Injectable() export class ProjectMetricsService { - constructor(private openSearchService: OpenSearchService) {} - - async getRequests( - projectId: string, - startDate: Date, - endDate: Date, - filters: FilterInput[] - ): Promise { - const { current, previous } = getStartAndEndDates(startDate, endDate); - - const buildAndExecuteQuery = async (startDate: string, endDate: string) => { - let body = mapFiltersToDql({ filters, restrictions: {} }); - body = buildBaseProjectMetricQuery(body, projectId, startDate, endDate); - - const result = await this.openSearchService.client.search({ - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }); - - return result.body.hits.total.value || 0; - }; - - const [currentRequestCount, previousRequestCount] = await Promise.all([ - buildAndExecuteQuery(current.startDate, current.endDate), - buildAndExecuteQuery(previous.startDate, previous.endDate), - ]); - - return { - currentValue: currentRequestCount, - previousValue: previousRequestCount, - }; - } - - async getCost( - projectId: string, - startDate: Date, - endDate: Date, - filters: FilterInput[] - ): Promise { - const { current, previous } = getStartAndEndDates(startDate, endDate); - - const buildAndExecuteQuery = async (startDate: string, endDate: string) => { - let body = mapFiltersToDql({ filters, restrictions: {} }); - body = buildBaseProjectMetricQuery( - body, - projectId, - startDate, - endDate - ).aggregation("sum", "calculated.totalCost", "total_cost"); - - const query = { - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }; - - const result = await this.openSearchService.client.search(query); - - return result.body.aggregations.total_cost.value || 0; - }; - - const [currentRequestCount, previousRequestCount] = await Promise.all([ - buildAndExecuteQuery(current.startDate, current.endDate), - buildAndExecuteQuery(previous.startDate, previous.endDate), - ]); - - return { - currentValue: parseFloat(currentRequestCount.toFixed(5)), - previousValue: parseFloat(previousRequestCount.toFixed(5)), - }; - } - - async getAvgDuration( - projectId: string, - startDate: Date, - endDate: Date, - filters: FilterInput[] - ): Promise { - const { current, previous } = getStartAndEndDates(startDate, endDate); - - const buildAndExecuteQuery = async (startDate: string, endDate: string) => { - let body = mapFiltersToDql({ filters, restrictions: {} }); - body = buildBaseProjectMetricQuery( - body, - projectId, - startDate, - endDate - ).aggregation("avg", "calculated.duration", "avg_duration"); - const result = await this.openSearchService.client.search({ - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }); - - return result.body.aggregations.avg_duration.value || 0; - }; + constructor( + private clickHouseService: ClickHouseService + ) {} - const [currentRequestCount, previousRequestCount] = await Promise.all([ - buildAndExecuteQuery(current.startDate, current.endDate), - buildAndExecuteQuery(previous.startDate, previous.endDate), - ]); - - return { - currentValue: parseInt(currentRequestCount), - previousValue: parseInt(previousRequestCount), - }; - } - - async getSuccessfulRequests( - projectId: string, - startDate: Date, - endDate: Date, - filters: FilterInput[] - ): Promise { - const { current, previous } = getStartAndEndDates(startDate, endDate); - - const buildAndExecuteQuery = async (startDate: string, endDate: string) => { - let body = mapFiltersToDql({ filters, restrictions: {} }); - body = buildBaseProjectMetricQuery( - body, - projectId, - startDate, - endDate - ).filter("term", "response.status", 200); - - const result = await this.openSearchService.client.search({ - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }); - - return result.body.hits.total.value || 0; - }; - - const [currentRequestCount, previousRequestCount] = await Promise.all([ - buildAndExecuteQuery(current.startDate, current.endDate), - buildAndExecuteQuery(previous.startDate, previous.endDate), - ]); - - return { - currentValue: parseInt(currentRequestCount), - previousValue: parseInt(previousRequestCount), - }; - } - - async getErroneousRequests( + async getGenericHistogram( projectId: string, + histogramId: HistogramIdType, startDate: Date, endDate: Date, + bucketSize: ProjectMetricHistogramBucketSize, filters: FilterInput[] - ): Promise { - const { current, previous } = getStartAndEndDates(startDate, endDate); - - const buildAndExecuteQuery = async (startDate: string, endDate: string) => { - let body = mapFiltersToDql({ filters, restrictions: {} }); - body = buildBaseProjectMetricQuery( - body, - projectId, - startDate, - endDate - ).notFilter("term", "response.status", 200); - - const result = await this.openSearchService.client.search({ - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }); - - return result.body.hits.total.value || 0; - }; - - const [currentRequestCount, previousRequestCount] = await Promise.all([ - buildAndExecuteQuery(current.startDate, current.endDate), - buildAndExecuteQuery(previous.startDate, previous.endDate), - ]); + ): Promise { + let queryBuilder: Knex.QueryBuilder; + + switch (histogramId) { + case HistogramIdType.requestDuration: + queryBuilder = averageRequestDurationQuery( + this.clickHouseService.knex, + { projectId, bucketSize, startDate, endDate } + ); + break; + case HistogramIdType.successErrorRate: + queryBuilder = successErrorRateQuery(this.clickHouseService.knex, { + projectId, + bucketSize, + startDate, + endDate, + }); + break; + default: + throw new BadRequestException( + `HistogramId ${histogramId} is not supported` + ); + } - return { - currentValue: parseInt(currentRequestCount), - previousValue: parseInt(previousRequestCount), - }; + const result = await queryBuilder; + return { data: result }; } - async getHistogram( + async getProjectMetricDelta( projectId: string, - metricType: ProjectMetricType, startDate: Date, endDate: Date, - bucketSize: string, - filters: FilterInput[] - ): Promise { - let body = mapFiltersToDql({ filters, restrictions: {} }); - - body = body - .addFilter("term", "ownership.projectId", projectId) - .andFilter("range", "timestamp", { - gte: startDate.toISOString(), - lte: endDate.toISOString(), - }); - - let aggType, aggField; - - switch (metricType) { - case ProjectMetricType.requests: - aggType = "value_count"; - aggField = "timestamp"; + metric: DeltaMetricType + ): Promise { + let selectStatement: string; + + switch (metric) { + case DeltaMetricType.AverageRequestDuration: + selectStatement = /*sql*/ ` + avgIf(r.duration, r.isError = false AND r.requestTimestamp >= currentStartDate AND r.responseTimestamp <= currentEndDate) AS currentValue, + avgIf(r.duration, r.isError = false AND r.requestTimestamp >= previousStartDate AND r.responseTimestamp <= previousEndDate) AS previousValue + `; + break; + case DeltaMetricType.TotalRequests: + selectStatement = /*sql*/ ` + countIf(r.requestTimestamp >= currentStartDate AND r.responseTimestamp <= currentEndDate) AS currentValue, + countIf(r.requestTimestamp >= previousStartDate AND r.responseTimestamp <= previousEndDate) AS previousValue + `; break; - case ProjectMetricType.duration: - aggType = "avg"; - aggField = "calculated.duration"; + case DeltaMetricType.TotalCost: + selectStatement = /*sql*/ ` + sumIf(r.totalCost, r.requestTimestamp >= currentStartDate AND r.responseTimestamp <= currentEndDate) AS currentValue, + sumIf(r.totalCost, r.requestTimestamp >= previousStartDate AND r.responseTimestamp <= previousEndDate) AS previousValue + `; break; - case ProjectMetricType.erroneousRequests: - aggType = "value_count"; - aggField = "timestamp"; - body = body.notFilter("term", "response.status", 200); + case DeltaMetricType.SuccessResponses: + selectStatement = /*sql*/ ` + countIf(r.isError = false AND r.requestTimestamp >= currentStartDate AND r.responseTimestamp <= currentEndDate) AS currentSuccess, + countIf(r.isError = false AND r.requestTimestamp >= previousStartDate AND r.responseTimestamp <= previousEndDate) AS previousSuccess, + countIf(r.requestTimestamp >= currentStartDate AND r.responseTimestamp <= currentEndDate) AS currentTotal, + countIf(r.requestTimestamp >= previousStartDate AND r.responseTimestamp <= previousEndDate) AS previousTotal, + if(currentTotal = 0, 0, (currentSuccess / currentTotal)) as currentValue, + if(previousTotal = 0, 0, (previousSuccess / previousTotal)) as previousValue + `; break; + default: + throw new BadRequestException(`Metric ${metric} is not supported`); } - body = body.aggregation( - "date_histogram", - "timestamp", - { - interval: bucketSize, - extended_bounds: { - min: startDate.toISOString(), - max: endDate.toISOString(), - }, - }, - "metrics_over_time", - (agg) => agg.aggregation(aggType, aggField, "metric_value") - ); - - body = body.size(0); - - const result = await this.openSearchService.client.search({ - index: this.openSearchService.requestsIndexAlias, - body: body.build(), - }); - - // Convert the response to the HistogramMetric format - const buckets = result.body.aggregations.metrics_over_time.buckets; - - return buckets.map((bucket) => ({ - date: bucket.key_as_string, - value: parseInt(bucket.metric_value.value || 0), - })); + const query = /*sql*/ ` + WITH ( + parseDateTimeBestEffort('${startDate.toISOString()}') AS currentStartDate, + parseDateTimeBestEffort('${endDate.toISOString()}') AS currentEndDate, + datediff(second, currentStartDate, currentEndDate) AS diff, + subtractSeconds(currentStartDate, diff) AS previousStartDate, + subtractSeconds(currentEndDate, diff) AS previousEndDate + ) + SELECT + ${selectStatement} + FROM + reports r + WHERE + projectId = '${projectId}' + `; + + try { + const result = await this.clickHouseService.knex.raw(query); + const data = result[0][0]; + + return { + currentValue: data.currentValue, + previousValue: data.previousValue, + }; + } catch (error) { + console.error(error); + throw new InternalServerErrorException(`Failed to get metric delta`); + } } } diff --git a/apps/server/src/app/metrics/queries/average-request-duration-query.ts b/apps/server/src/app/metrics/queries/average-request-duration-query.ts new file mode 100644 index 00000000..6b5178b2 --- /dev/null +++ b/apps/server/src/app/metrics/queries/average-request-duration-query.ts @@ -0,0 +1,37 @@ +import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; +import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { Knex } from "knex"; + +export const averageRequestDurationQuery = ( + knex: Knex, + params: { + projectId: string; + bucketSize: ProjectMetricHistogramBucketSize; + startDate: Date; + endDate: Date; + } +): Knex.QueryBuilder => { + const { projectId, bucketSize, startDate, endDate } = params; + + const timeProps = timePropertiesFromBucketSize(bucketSize); + const bucketsSubquery = generateBucketsSubquery(knex, { + startDate, + endDate, + timeProps, + }); + + const mainquery = knex + .with("buckets", bucketsSubquery) + .select({ + timestamp: "b.timestamp", + value: knex.raw(`COALESCE(AVG(r.duration), 0)`), + }) + .from("buckets as b") + .leftJoin("reports as r", (join) => + join.on(knex.raw(`${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'`)) + ) + .groupBy("b.timestamp") + .orderBy("b.timestamp"); + + return mainquery; +}; \ No newline at end of file diff --git a/apps/server/src/app/metrics/queries/model-usage-query.ts b/apps/server/src/app/metrics/queries/model-usage-query.ts new file mode 100644 index 00000000..c462487e --- /dev/null +++ b/apps/server/src/app/metrics/queries/model-usage-query.ts @@ -0,0 +1,43 @@ +import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; +import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { Knex } from "knex"; + +export const modelUsageQuery = ( + knex: Knex, + params: { + projectId: string; + bucketSize: ProjectMetricHistogramBucketSize; + column: string; + startDate: Date; + endDate: Date; + } +): Knex.QueryBuilder => { + const { projectId, bucketSize, column, startDate, endDate } = params; + + const timeProps = timePropertiesFromBucketSize(bucketSize); + const bucketsSubquery = generateBucketsSubquery(knex, { + startDate, + endDate, + timeProps, + }); + + const mainquery = knex + .select({ + timestamp: "s.timeframe", + values: knex.raw(` + if( + arrayReduce('sumMap', arrayMap((model, duration) -> map(model, duration), groupArray(r.model), groupArray(r.duration))) = map('', 0), + NULL, + toJSONString(arrayReduce('sumMap', arrayMap((model, duration) -> map(model, duration), groupArray(r.model), groupArray(r.duration)))) + ) + `) + }) + .from(bucketsSubquery) + .leftJoin("reports as r", (join) => + join.on(knex.raw(`${timeProps.roundFn}(r."request.timestamp") = s.timeframe AND r."projectId" = '${projectId}'`)) + ) + .groupBy(["s.timeframe"]) + .orderBy(["s.timeframe"]) + + return mainquery; +}; diff --git a/apps/server/src/app/metrics/queries/queries.utils.ts b/apps/server/src/app/metrics/queries/queries.utils.ts new file mode 100644 index 00000000..4bcc3b36 --- /dev/null +++ b/apps/server/src/app/metrics/queries/queries.utils.ts @@ -0,0 +1,51 @@ +import { Knex } from "knex"; +import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; + +interface TimeProps { + roundFn: string; + interval: string; +} + +export const timePropertiesFromBucketSize = ( + bucketSize: ProjectMetricHistogramBucketSize +): TimeProps => { + switch (bucketSize) { + case ProjectMetricHistogramBucketSize.minutely: + return { roundFn: "toStartOfMinute", interval: "minute" }; + case ProjectMetricHistogramBucketSize.hourly: + return { roundFn: "toStartOfHour", interval: "hour" }; + case ProjectMetricHistogramBucketSize.daily: + return { roundFn: "toStartOfDay", interval: "day" }; + case ProjectMetricHistogramBucketSize.weekly: + return { roundFn: "toStartOfWeek", interval: "day" }; + case ProjectMetricHistogramBucketSize.monthly: + return { roundFn: "toStartOfMonth", interval: "month" }; + } +}; + +export const generateBucketsSubquery = ( + knex: Knex, + params: { + startDate: Date; + endDate: Date; + timeProps: TimeProps; + } +): Knex.QueryBuilder => { + const { startDate, endDate, timeProps } = params; + + const subquery = knex + .select( + knex.raw( + `${timeProps.roundFn}(parseDateTimeBestEffort(?) + INTERVAL number hour) AS timestamp`, + [startDate] + ) + ) + .from( + knex.raw( + "numbers(dateDiff('hour', parseDateTimeBestEffort(?), parseDateTimeBestEffort(?)) + 1)", + [startDate, endDate] + ) + ); + + return subquery; +}; diff --git a/apps/server/src/app/metrics/queries/success-error-rate-query.ts b/apps/server/src/app/metrics/queries/success-error-rate-query.ts new file mode 100644 index 00000000..19b4f238 --- /dev/null +++ b/apps/server/src/app/metrics/queries/success-error-rate-query.ts @@ -0,0 +1,42 @@ +import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; +import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { Knex } from "knex"; + +export const successErrorRateQuery = ( + knex: Knex, + params: { + projectId: string; + bucketSize: ProjectMetricHistogramBucketSize; + startDate: Date; + endDate: Date; + } +): Knex.QueryBuilder => { + const { projectId, bucketSize, startDate, endDate } = params; + + const timeProps = timePropertiesFromBucketSize(bucketSize); + const bucketsSubquery = generateBucketsSubquery(knex, { + startDate, + endDate, + timeProps, + }); + + const mainquery = knex + .with( + "buckets", + bucketsSubquery + ) + .select({ + timestamp: "b.timestamp", + requests: knex.raw("count(*)"), + error: knex.raw("countIf(r.isError = true AND toStartOfHour(r.requestTimestamp) = timestamp)"), + success: knex.raw("countIf(r.isError = false AND toStartOfHour(r.requestTimestamp) = timestamp)"), + }) + .from("buckets as b") + .leftJoin("reports as r", (join) => + join.on(knex.raw(`${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'`)) + ) + .groupBy("timestamp") + .orderBy("timestamp"); + + return mainquery; +}; diff --git a/apps/server/src/app/opensearch/create-indexes.ts b/apps/server/src/app/opensearch/create-indexes.ts deleted file mode 100644 index 110be44e..00000000 --- a/apps/server/src/app/opensearch/create-indexes.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Client } from "@opensearch-project/opensearch"; -import { pino } from "pino"; - -export async function createIndexes( - indexName: string, - client: Client, - _logger: pino.Logger -) { - const logger = _logger.child({ step: "createIndexes" }); - logger.info("Creating indexes"); - await createRequestsIndex(indexName, client, logger); -} - -export async function createRequestsIndex( - name: string, - client: Client, - _logger: pino.Logger -) { - const logger = _logger.child({ index: "createRequestsIndex" }); - const index = name; - - const indexExists = await client.indices.exists({ - index, - }); - - if (!indexExists.body) { - logger.info("Creating index"); - - await client.indices.create({ - index, - }); - } else { - logger.info("Index already exists, skipping creation"); - } - - logger.info("Index created"); - logger.info("Setting/Updating mappings"); - - await client.indices.putMapping({ - index, - body: { - properties: { - ownership: { - properties: { - organizationId: { - type: "keyword", - }, - projectId: { - type: "keyword", - }, - }, - }, - reportId: { - type: "keyword", - }, - calculated: { - properties: { - promptCost: { - type: "float", - }, - completionCost: { - type: "float", - }, - totalCost: { - type: "float", - }, - promptTokens: { - type: "integer", - }, - completionTokens: { - type: "integer", - }, - totalTokens: { - type: "integer", - }, - duration: { - type: "integer", - }, - }, - }, - properties: { - type: "object", - }, - metadata: { - properties: { - promptId: { - type: "keyword", - }, - promptVersionSha: { - type: "keyword", - }, - provider: { - type: "keyword", - }, - type: { - type: "keyword", - }, - isTestPrompt: { - type: "boolean", - }, - client: { - type: "keyword", - }, - clientVersion: { - type: "keyword", - }, - }, - }, - cacheEnabled: { - type: "boolean", - }, - cacheHit: { - type: "boolean", - }, - request: { - properties: { - timestamp: { - type: "date", - }, - body: { - properties: { - messages: { - enabled: false, - }, - }, - }, - }, - }, - response: { - properties: { - timestamp: { - type: "date", - }, - status: { - type: "long", - }, - body: { - type: "nested", - }, - }, - }, - }, - }, - }); -} diff --git a/apps/server/src/app/opensearch/opensearch.module.ts b/apps/server/src/app/opensearch/opensearch.module.ts deleted file mode 100644 index 793f66b9..00000000 --- a/apps/server/src/app/opensearch/opensearch.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from "@nestjs/common"; -import { LoggerModule } from "../logger/logger.module"; -import { OpenSearchService } from "./opensearch.service"; - -@Module({ - imports: [LoggerModule], - providers: [OpenSearchService], - exports: [OpenSearchService], -}) -export class OpenSearchModule {} diff --git a/apps/server/src/app/opensearch/opensearch.service.ts b/apps/server/src/app/opensearch/opensearch.service.ts deleted file mode 100644 index 6208917c..00000000 --- a/apps/server/src/app/opensearch/opensearch.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ConfigService } from "@nestjs/config"; -import { Injectable, OnModuleInit } from "@nestjs/common"; -import { Client } from "@opensearch-project/opensearch"; -import { AwsSigv4Signer } from "@opensearch-project/opensearch/aws"; -import { defaultProvider } from "@aws-sdk/credential-provider-node"; -import { createLogger } from "../logger/create-logger"; -import { pino } from "pino"; -import { createIndexes } from "./create-indexes"; - -@Injectable() -export class OpenSearchService implements OnModuleInit { - public client: Client; - private logger: pino.Logger; - public requestsIndexAlias: string; - - constructor(private config: ConfigService) { - this.logger = createLogger({ - scope: "OpenSearchService", - }); - - this.requestsIndexAlias = config.get("OPENSEARCH_INDEX_REQUESTS_ALIAS"); - } - - async onModuleInit() { - const node = this.config.get("OPENSEARCH_URL"); - const authMode = this.config.get("OPENSEARCH_AUTH"); - - if (authMode === "insecure") { - this.logger.info("Creating OpenSearch client in insecure mode"); - this.client = new Client({ - node, - }); - } - - if (authMode === "aws") { - this.logger.info("Creating OpenSearch client using AWS credentials"); - const signer = AwsSigv4Signer({ - region: "us-east-1", - getCredentials: async () => { - const provider = defaultProvider(); - return provider(); - }, - }); - - this.client = new Client({ - ...signer, - node, - }); - } - - await this.healthCheck(); - const alias = this.config.get("OPENSEARCH_INDEX_REQUESTS_ALIAS"); - await createIndexes(alias, this.client, this.logger); - } - - async healthCheck() { - try { - const { meta } = await this.client.cluster.health(); - this.logger.info( - { status: meta.connection.status }, - "OpenSearch healthcheck" - ); - } catch (error) { - this.logger.error({ error }, "OpenSearch healthcheck failed"); - throw error; - } - } -} diff --git a/apps/server/src/app/prompt-tester/prompt-tester.resolver.ts b/apps/server/src/app/prompt-tester/prompt-tester.resolver.ts index 866dd893..881f234d 100644 --- a/apps/server/src/app/prompt-tester/prompt-tester.resolver.ts +++ b/apps/server/src/app/prompt-tester/prompt-tester.resolver.ts @@ -9,6 +9,8 @@ import { RequestUser } from "../identity/users.types"; import { PinoLogger } from "../logger/pino-logger"; import { isOrgMemberOrThrow } from "../identity/identity.utils"; import { RequestReport } from "../reporting/object-types/request-report.model"; +import { SerializedReport } from "@pezzo/types"; +import GraphQLJSON from "graphql-type-json"; @UseGuards(AuthGuard) @Resolver(() => RequestReport) @@ -19,11 +21,11 @@ export class PromptTesterResolver { private logger: PinoLogger ) {} - @Mutation(() => RequestReport) + @Mutation(() => GraphQLJSON) async testPrompt( @Args("data") data: TestPromptInput, @CurrentUser() user: RequestUser - ): Promise { + ): Promise { this.logger .assign({ projectId: data.projectId, diff --git a/apps/server/src/app/prompt-tester/prompt-tester.service.ts b/apps/server/src/app/prompt-tester/prompt-tester.service.ts index fe9f553e..384de765 100644 --- a/apps/server/src/app/prompt-tester/prompt-tester.service.ts +++ b/apps/server/src/app/prompt-tester/prompt-tester.service.ts @@ -2,8 +2,8 @@ import { Injectable } from "@nestjs/common"; import { TestPromptInput } from "../prompts/inputs/test-prompt.input"; import { Pezzo, PezzoOpenAI } from "@pezzo/client"; import { ReportingService } from "../reporting/reporting.service"; -import { RequestReport } from "../reporting/object-types/request-report.model"; import { ProviderApiKeysService } from "../credentials/provider-api-keys.service"; +import { SerializedReport } from "@pezzo/types"; @Injectable() export class PromptTesterService { @@ -16,7 +16,7 @@ export class PromptTesterService { testData: TestPromptInput, projectId: string, organizationId: string - ): Promise { + ): Promise { const provider = "OpenAI"; const providerApiKey = await this.providerApiKeysService.getByProvider( provider, diff --git a/apps/server/src/app/prompts/prompt-versions.resolver.ts b/apps/server/src/app/prompts/prompt-versions.resolver.ts index 3a785cd8..07a98d22 100644 --- a/apps/server/src/app/prompts/prompt-versions.resolver.ts +++ b/apps/server/src/app/prompts/prompt-versions.resolver.ts @@ -108,7 +108,6 @@ export class PromptVersionsResolver { @ResolveField(() => ExtendedUser) async createdBy(@Parent() parent: PromptVersion) { const user = await this.usersService.getById(parent.createdById); - console.log("user", user); const extendedUser = this.usersService.serializeExtendedUser(user); return extendedUser; } diff --git a/apps/server/src/app/reporting/dto/create-report.dto.ts b/apps/server/src/app/reporting/dto/create-report.dto.ts index ddb29b87..9e2d3b7c 100644 --- a/apps/server/src/app/reporting/dto/create-report.dto.ts +++ b/apps/server/src/app/reporting/dto/create-report.dto.ts @@ -18,13 +18,26 @@ import { ApiProperty } from "@nestjs/swagger"; export class PromptExecutionMetadataDto { @ApiProperty({ - description: "LLM provider", + description: "Model", type: String, - enum: Provider, }) - @IsEnum(Provider) - provider: Provider; + @IsString() + model: string; + @ApiProperty({ + description: "Model Author", + type: String, + }) + @IsString() + modelAuthor: string; + + @ApiProperty({ + description: "Execution provider", + type: String, + }) + @IsString() + provider: string + @ApiProperty({ description: "Client name identifier", type: String, @@ -151,3 +164,18 @@ export class CreateReportDto< @IsBoolean() cacheHit?: boolean = null; } + +export class ExecutionCalculatedDto { + promptCost: number; + completionCost: number; + totalCost: number; + promptTokens: number; + completionTokens: number; + totalTokens: number; + duration: number; +} + +export class CreateReportV3Dto extends CreateReportDto { + @ValidateNested({ each: true }) + calculated: ExecutionCalculatedDto; +} \ No newline at end of file diff --git a/apps/server/src/app/reporting/inputs/get-requests.input.ts b/apps/server/src/app/reporting/inputs/get-requests.input.ts index 4d82bcc4..5445bc8c 100644 --- a/apps/server/src/app/reporting/inputs/get-requests.input.ts +++ b/apps/server/src/app/reporting/inputs/get-requests.input.ts @@ -19,3 +19,12 @@ export class GetRequestsInput { @Field(() => SortInput, { nullable: true }) sort?: SortInput; } + +@InputType() +export class GetReportInput { + @Field(() => String, { nullable: false }) + projectId: string; + + @Field(() => String, { nullable: false }) + reportId: string; +} diff --git a/apps/server/src/app/reporting/object-types/request-report-result.model.ts b/apps/server/src/app/reporting/object-types/request-report-result.model.ts index 4c11a55c..df91fe22 100644 --- a/apps/server/src/app/reporting/object-types/request-report-result.model.ts +++ b/apps/server/src/app/reporting/object-types/request-report-result.model.ts @@ -1,13 +1,19 @@ import { Field, ObjectType } from "@nestjs/graphql"; -import { RequestReport } from "./request-report.model"; import { Pagination } from "../../../lib/pagination"; -import { PaginationResult } from "../../../lib/ts-helpers"; +import { GraphQLJSONObject } from "graphql-type-json"; +import { SerializedPaginatedReport, SerializedReport } from "@pezzo/types"; @ObjectType() -export class RequestReportResult implements PaginationResult { - @Field(() => [RequestReport], { nullable: false }) - data: RequestReport[]; +export class PaginatedReportsResult { + @Field(() => [GraphQLJSONObject], { nullable: false }) + data: SerializedPaginatedReport[]; @Field(() => Pagination, { nullable: false }) pagination: Pagination; } + +@ObjectType() +export class ReportResult { + @Field(() => GraphQLJSONObject, { nullable: false }) + data: SerializedReport; +} \ No newline at end of file diff --git a/apps/server/src/app/reporting/reporting.controller.ts b/apps/server/src/app/reporting/reporting.controller.ts index 09426011..e92ceb02 100644 --- a/apps/server/src/app/reporting/reporting.controller.ts +++ b/apps/server/src/app/reporting/reporting.controller.ts @@ -41,7 +41,7 @@ export class ReportingController { organizationId, projectId, }) - .info("Saving report to OpenSearch"); + .info("Saving report"); try { return this.reportingService.saveReport(dto, { @@ -49,7 +49,7 @@ export class ReportingController { projectId, }); } catch (error) { - this.logger.error(error, "Error saving report to OpenSearch"); + this.logger.error(error, "Error saving report"); throw new InternalServerErrorException(); } } diff --git a/apps/server/src/app/reporting/reporting.module.ts b/apps/server/src/app/reporting/reporting.module.ts index 7dba5ba5..e09f6d3c 100644 --- a/apps/server/src/app/reporting/reporting.module.ts +++ b/apps/server/src/app/reporting/reporting.module.ts @@ -4,11 +4,11 @@ import { AuthModule } from "../auth/auth.module"; import { LoggerModule } from "../logger/logger.module"; import { IdentityModule } from "../identity/identity.module"; import { ReportingService } from "./reporting.service"; -import { OpenSearchModule } from "../opensearch/opensearch.module"; import { RequestReportsResolver } from "./requests.resolver"; +import { ClickhHouseModule } from "../clickhouse/clickhouse.module"; @Module({ - imports: [OpenSearchModule, LoggerModule, AuthModule, IdentityModule], + imports: [ClickhHouseModule, LoggerModule, AuthModule, IdentityModule], exports: [ReportingService], controllers: [ReportingController], providers: [ReportingService, RequestReportsResolver], diff --git a/apps/server/src/app/reporting/reporting.service.ts b/apps/server/src/app/reporting/reporting.service.ts index 51df27ff..a6ef729d 100644 --- a/apps/server/src/app/reporting/reporting.service.ts +++ b/apps/server/src/app/reporting/reporting.service.ts @@ -1,21 +1,30 @@ -import { OpenSearchService } from "../opensearch/opensearch.service"; -import { Injectable } from "@nestjs/common"; +import { + BadRequestException, + Injectable, + InternalServerErrorException, +} from "@nestjs/common"; import { CreateReportDto } from "./dto/create-report.dto"; import { randomUUID } from "crypto"; import { buildRequestReport } from "./utils/build-request-report"; -import { RequestReport } from "./object-types/request-report.model"; import { MAX_PAGE_SIZE } from "../../lib/pagination"; -import { FilterInput } from "../common/filters/filter.input"; +import { + FilterInput, + getSQLOperatorByFilterOperator, +} from "../common/filters/filter.input"; import { SortInput } from "../common/filters/sort.input"; -import { mapFiltersToDql } from "./utils/dql-utils"; -import { AnalyticsService } from "../analytics/analytics.service"; +import { ClickHouseService } from "../clickhouse/clickhouse.service"; +import { + PaginatedReportsSchema, + ReportSchema, + SerializedReport, + serializePaginatedReport, + serializeReport, +} from "@pezzo/types"; +import { PaginatedReportsResult } from "./object-types/request-report-result.model"; @Injectable() export class ReportingService { - constructor( - private openSearchService: OpenSearchService, - private analytics: AnalyticsService - ) {} + constructor(private clickHouseService: ClickHouseService) {} async saveReport( dto: CreateReportDto, @@ -24,92 +33,164 @@ export class ReportingService { projectId: string; }, isTestPrompt = false - ): Promise { + ): Promise { const reportId = randomUUID(); const { report, calculated } = buildRequestReport(dto); - const { properties, metadata, request, response, cacheEnabled, cacheHit } = - report; - - await this.openSearchService.client.index({ - index: this.openSearchService.requestsIndexAlias, - body: { - timestamp: request.timestamp, - ownership, - reportId, - calculated, - properties, - metadata, - request, - response, - cacheEnabled, - cacheHit, - }, - }); - - this.analytics.trackEvent("request_reported", { + const { metadata, request, response, cacheEnabled, cacheHit } = report; + + const reportToSave: ReportSchema = { + id: reportId, + timestamp: request.timestamp, organizationId: ownership.organizationId, projectId: ownership.projectId, - reportId, - isTestPrompt: isTestPrompt, - promptId: dto.metadata.promptId as string, - client: (dto.metadata.client as string) || null, - clientVersion: (dto.metadata.clientVersion as string) || null, - cacheEnabled, - }); - - return { - reportId, - calculated, - properties, - metadata: metadata as unknown as RequestReport["metadata"], - request: request as any, - response: response as any, - cacheEnabled, - cacheHit, + promptCost: (calculated as any).promptCost, + completionCost: (calculated as any).completionCost, + totalCost: (calculated as any).totalCost, + promptTokens: (calculated as any).promptTokens, + completionTokens: (calculated as any).completionTokens, + totalTokens: (calculated as any).totalTokens, + duration: (calculated as any).duration, + environment: isTestPrompt ? "PLAYGROUND" : metadata.environment, + client: metadata.client, + clientVersion: metadata.clientVersion, + model: request.body.model, + provider: metadata.provider, + modelAuthor: "openai", + type: "ChatCompletion", + requestTimestamp: request.timestamp, + requestBody: JSON.stringify(request.body), + isError: (response as any).status !== 200 ? 1 : 0, + responseStatusCode: (response as any).status, + responseTimestamp: response.timestamp, + responseBody: JSON.stringify(response.body), + cacheEnabled: cacheEnabled ? 1 : 0, + cacheHit: cacheHit ? 1 : 0, + promptId: report.metadata.promptId || null, }; + + try { + await this.clickHouseService.client.insert({ + format: "JSONEachRow", + table: "reports", + values: [reportToSave], + }); + } catch (error) { + console.error(error); + throw new InternalServerErrorException(`Could not save report`); + } + + return serializeReport(reportToSave); + } + + async getReportById( + reportId: string, + projectId: string + ): Promise { + const rows = await this.clickHouseService.knex + .select("*") + .from("reports") + .where({ + id: reportId, + projectId, + }) + .limit(1); + + const result = serializeReport(rows[0]); + return result; } async getReports({ projectId, - organizationId, offset, limit, - filters, + filters = [], sort, }: { projectId: string; - organizationId: string; offset: number; limit: number; filters: FilterInput[]; sort: SortInput; - }) { + }): Promise { const size = limit > MAX_PAGE_SIZE ? MAX_PAGE_SIZE : limit; const from = offset > 0 ? offset : 0; - const body = mapFiltersToDql({ - restrictions: { - "ownership.projectId": projectId, - "ownership.organizationId": organizationId, - }, - sort, - filters, - }); - - const dql = body.build(); - - return this.openSearchService.client.search<{ - hits: { - hits: Array<{ _source: RequestReport }>; - total: { value: number }; + const knex = this.clickHouseService.knex; + + let totalRowsQuery = knex + .select(knex.raw("COUNT() as total")) + .from("reports") + .where("projectId", projectId) + .first(); + + let query = knex + .select({ + id: "id", + environment: "environment", + timestamp: "timestamp", + responseStatusCode: "responseStatusCode", + model: "model", + modelAuthor: "modelAuthor", + provider: "provider", + duration: "duration", + totalTokens: "totalTokens", + totalCost: "totalCost", + cacheEnabled: "cacheEnabled", + cacheHit: "cacheHit", + }) + .from("reports"); + + query = query.where("projectId", "=", projectId); + + for (const filter of filters) { + try { + const operator = getSQLOperatorByFilterOperator(filter.operator); + let field = filter.field; + let value = filter.value; + + if (filter.field === ("timestamp" as any)) { + const d = new Date(value as string).toISOString(); + value = knex.raw(`parseDateTimeBestEffort('${d}')`) as any; + } + + if (filter.operator === "like") { + field = knex.raw(`lower(${filter.field})`) as any; + value = (value as string).toLowerCase(); + } + + query = query.where(field, operator, value); + totalRowsQuery = totalRowsQuery.where(field, operator, value); + } catch (error) { + throw new BadRequestException( + `Invalid filter operator ${filter.operator}` + ); + } + } + + query = query.limit(size).offset(offset); + + if (sort) { + query.orderBy(sort.field, sort.order) + } else { + query.orderBy("timestamp", "desc") + } + + try { + const result = await query; + const totalRowsResult = await totalRowsQuery; + + return { + data: result.map((report) => serializePaginatedReport(report)), + pagination: { + offset: from, + limit, + total: totalRowsResult.total, + }, }; - }>({ - index: this.openSearchService.requestsIndexAlias, - body: dql, - size, - from, - track_total_hits: true, - }); + } catch (error) { + console.error(error); + throw new InternalServerErrorException(`Could not get reports`); + } } } diff --git a/apps/server/src/app/reporting/requests.resolver.ts b/apps/server/src/app/reporting/requests.resolver.ts index c7965b36..1df28686 100644 --- a/apps/server/src/app/reporting/requests.resolver.ts +++ b/apps/server/src/app/reporting/requests.resolver.ts @@ -11,9 +11,11 @@ import { PinoLogger } from "../logger/pino-logger"; import { CurrentUser } from "../identity/current-user.decorator"; import { RequestUser } from "../identity/users.types"; import { isOrgMemberOrThrow } from "../identity/identity.utils"; -import { GetRequestsInput } from "./inputs/get-requests.input"; +import { GetReportInput, GetRequestsInput } from "./inputs/get-requests.input"; import { ProjectsService } from "../identity/projects.service"; -import { RequestReportResult } from "./object-types/request-report-result.model"; +import { PaginatedReportsResult } from "./object-types/request-report-result.model"; +import GraphQLJSON from "graphql-type-json"; +import { SerializedReport } from "@pezzo/types"; @UseGuards(AuthGuard) @Resolver(() => RequestReport) @@ -24,12 +26,13 @@ export class RequestReportsResolver { private readonly logger: PinoLogger ) {} - @Query(() => RequestReportResult) - async paginatedRequests( - @Args("data") data: GetRequestsInput, + @Query(() => GraphQLJSON) + async report( + @Args("data") data: GetReportInput, @CurrentUser() user: RequestUser - ): Promise { + ): Promise { let project; + try { project = await this.projectsService.getProjectById(data.projectId); @@ -43,27 +46,38 @@ export class RequestReportsResolver { throw new InternalServerErrorException(); } + const result = await this.reportingService.getReportById(data.reportId, data.projectId); + return result; + } + + @Query(() => PaginatedReportsResult) + async paginatedRequests( + @Args("data") data: GetRequestsInput, + @CurrentUser() user: RequestUser + ): Promise { + let project; + try { - const response = await this.reportingService.getReports({ - projectId: data.projectId, - organizationId: project.organizationId, - offset: data.offset, - limit: data.limit, - filters: data.filters, - sort: data.sort, - }); + project = await this.projectsService.getProjectById(data.projectId); + + if (!project) { + throw new NotFoundException(); + } - return { - data: response.body.hits.hits.map((hit) => hit._source), - pagination: { - offset: data.offset, - limit: data.limit, - total: response.body.hits.total.value, - }, - }; + isOrgMemberOrThrow(user, project.organizationId); } catch (error) { - this.logger.error(error, "Error getting reports from OpenSearch"); + this.logger.error(error, "Error getting projects"); throw new InternalServerErrorException(); } + + const result = await this.reportingService.getReports({ + projectId: data.projectId, + offset: data.offset, + limit: data.limit, + filters: data.filters, + sort: data.sort, + }); + + return result; } } diff --git a/apps/server/src/app/reporting/utils/build-request-report.ts b/apps/server/src/app/reporting/utils/build-request-report.ts index 5390d52e..d14824ba 100644 --- a/apps/server/src/app/reporting/utils/build-request-report.ts +++ b/apps/server/src/app/reporting/utils/build-request-report.ts @@ -47,6 +47,8 @@ const buildOpenAIReport = ( promptCost: parseFloat(promptCost.toFixed(6)), completionCost: parseFloat(completionCost.toFixed(6)), totalCost: parseFloat((promptCost + completionCost).toFixed(6)), + promptTokens: usage.prompt_tokens, + completionTokens: usage.completion_tokens, totalTokens, duration: requestDuration, }; diff --git a/apps/server/src/app/reporting/utils/dql-utils.ts b/apps/server/src/app/reporting/utils/dql-utils.ts deleted file mode 100644 index 855000f5..00000000 --- a/apps/server/src/app/reporting/utils/dql-utils.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { FilterInput, FilterOperator } from "../../common/filters/filter.input"; -import { SortInput } from "../../common/filters/sort.input"; -import bodybuilder from "bodybuilder"; - -const FILTER_FIELDS_ALLOW_LIST = new Set([ - "reportId", - "calculated.*", - "provider", - "type", - "properties.*", - "metadata.*", - "request.timestamp", - "response.timestamp", - "response.status", -]); - -function isValidFilterField(field: string): boolean { - // Check if the field is directly in the allow list - if (FILTER_FIELDS_ALLOW_LIST.has(field)) { - return true; - } - - // If the field is not directly in the allow list, check for wildcard matches - const fieldParts = field.split("."); - - // Build a filter field to check against the allow list, starting with the first part of the field - let checkField = fieldParts[0]; - - for (let i = 1; i < fieldParts.length; i++) { - // Add a wildcard to the checkField and see if it's in the allow list - checkField += ".*"; - if (FILTER_FIELDS_ALLOW_LIST.has(checkField)) { - return true; - } - - // If we didn't find a match with the wildcard, add the next part of the field to checkField and continue - checkField = `${checkField.slice(0, -1)}.${fieldParts[i]}`; - } - - // If we've gone through all parts of the field and didn't find a match in the allow list, the field is not allowed - return false; -} - -export const mapFiltersToDql = ({ - restrictions, - filters, - sort, -}: { - restrictions: Record; - filters?: FilterInput[]; - sort?: SortInput; -}) => { - let body = bodybuilder(); - - for (const key in restrictions) { - body = body.query("match", key, restrictions[key]); - } - - if (sort != null) { - body = body.sort(sort.field, sort.order); - } else { - body = body.sort("request.timestamp", "desc"); - } - - filters?.forEach((filter) => { - if (!isValidFilterField(filter.field)) return; - - switch (filter.operator.toLowerCase()) { - case FilterOperator.eq: - body = body.filter( - "term", - filter.field, - String(filter.value).toLowerCase() - ); - break; - case FilterOperator.neq: - body = body.notFilter("term", filter.field, filter.value); - break; - case FilterOperator.in: - // Ensure that filter.value is an array for 'in' operator - if (!Array.isArray(filter.value)) { - throw new Error(`Operator 'in' requires an array of values.`); - } - body = body.filter( - "terms", - filter.field, - filter.value.map((value) => String(value).toLowerCase()) - ); - break; - case FilterOperator.nin: - // Ensure that filter.value is an array for 'nin' operator - if (!Array.isArray(filter.value)) { - throw new Error(`Operator 'nin' requires an array of values.`); - } - body = body.notFilter( - "terms", - filter.field, - filter.value.map((value) => String(value).toLowerCase()) - ); - break; - case FilterOperator.contains: - body = body.query("match", filter.field, filter.value); - break; - case FilterOperator.gt: - case FilterOperator.gte: - case FilterOperator.lt: - case FilterOperator.lte: - body = body.filter("range", filter.field, { - [filter.operator]: filter.value, - }); - break; - default: - throw new Error(`Unknown filter operator: ${filter.operator}`); - } - }); - - return body; -}; diff --git a/clickhouse/config/config.d/config.xml b/clickhouse/config/config.d/config.xml new file mode 100644 index 00000000..a9101ae6 --- /dev/null +++ b/clickhouse/config/config.d/config.xml @@ -0,0 +1,3 @@ + + 9004 + \ No newline at end of file diff --git a/clickhouse/knexfile.ts b/clickhouse/knexfile.ts new file mode 100644 index 00000000..8f27bb9f --- /dev/null +++ b/clickhouse/knexfile.ts @@ -0,0 +1,23 @@ +import { Knex } from "knex"; +import clickhouse from "@pezzo/knex-clickhouse-dialect"; + +const { + CLICKHOUSE_HOST = "localhost", + CLICKHOUSE_PORT = "8123", + CLICKHOUSE_USER = "default", + CLICKHOUSE_PASSWORD = "default", +} = process.env; + +const config: { [key: string]: Knex.Config } = { + default: { + client: clickhouse as any, + connection: () => + `clickhouse://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default` as any, + migrations: { + disableTransactions: true, + disableMigrationsListValidation: true, + }, + }, +}; + +module.exports = config; diff --git a/clickhouse/migrations/20231231065935_create_reports.ts b/clickhouse/migrations/20231231065935_create_reports.ts new file mode 100644 index 00000000..9ddbf697 --- /dev/null +++ b/clickhouse/migrations/20231231065935_create_reports.ts @@ -0,0 +1,40 @@ +import type { Knex } from "knex"; + +export async function up(knex: Knex): Promise { + /* Create reports table */ + await knex.schema.createTable("reports", (table) => { + table.string("id") + table.dateTime("timestamp") + table.string("environment") + table.string("organizationId") + table.string("projectId") + table.specificType("promptTokens", "Float64") + table.specificType("completionTokens", "Float64") + table.specificType("totalTokens", "Float64") + table.specificType("promptCost", "Float64") + table.specificType("completionCost", "Float64") + table.specificType("totalCost", "Float64") + table.specificType("duration", "UInt32") + table.string("type") + table.string("client") + table.string("clientVersion") + table.string("model") + table.string("provider") + table.string("modelAuthor") + table.dateTime("requestTimestamp") + table.string("requestBody") + table.boolean("isError") + table.specificType("responseStatusCode", "UInt32") + table.dateTime("responseTimestamp") + table.string("responseBody") + table.boolean("cacheEnabled") + table.boolean("cacheHit") + }); +} + + +export async function down(knex: Knex): Promise { + /* Drop reports table */ + await knex.schema.dropTable("reports"); +} + diff --git a/clickhouse/migrations/20231231100122_create_report_properties.ts b/clickhouse/migrations/20231231100122_create_report_properties.ts new file mode 100644 index 00000000..c07716cf --- /dev/null +++ b/clickhouse/migrations/20231231100122_create_report_properties.ts @@ -0,0 +1,18 @@ +import type { Knex } from "knex"; + +export async function up(knex: Knex): Promise { + /* Create report properties table */ + await knex.schema.createTable("reportProperties", (table) => { + table.string("id").defaultTo(knex.raw("generateUUIDv4()")) + table.string("reportId") + table.string("key") + table.string("value") + }); +} + + +export async function down(knex: Knex): Promise { + /* Drop report properties table */ + await knex.schema.dropTable("reportProperties"); +} + diff --git a/clickhouse/package-lock.json b/clickhouse/package-lock.json new file mode 100644 index 00000000..b968d2e8 --- /dev/null +++ b/clickhouse/package-lock.json @@ -0,0 +1,958 @@ +{ + "name": "clickhouse", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "clickhouse", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@pezzo/knex-clickhouse-dialect": "^1.2.0" + }, + "devDependencies": { + "ts-node": "^10.9.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@pezzo/knex-clickhouse-dialect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", + "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "dependencies": { + "clickhouse": "^2.6.0", + "knex": "^2.5.1", + "lodash": "^4.17.21", + "sqlstring": "^2.3.3" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dev": true, + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/clickhouse": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/clickhouse/-/clickhouse-2.6.0.tgz", + "integrity": "sha512-HC5OV99GJOup4qZsTuWWPpXlj+847Z0OeygDU2x22rNYost0V/vWapzFWYZdV/5iRbGMrhFQPOyQEzmGvoaWRQ==", + "dependencies": { + "JSONStream": "1.3.4", + "lodash": "4.17.21", + "querystring": "0.2.0", + "request": "2.88.0", + "stream2asynciter": "1.0.3", + "through": "2.3.8", + "tsv": "0.2.0", + "uuid": "3.4.0" + } + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", + "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/knex": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", + "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.1", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream2asynciter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream2asynciter/-/stream2asynciter-1.0.3.tgz", + "integrity": "sha512-9/dEZW+LQjuW6ub5hmWi4n9Pn8W8qA8k7NAE1isecesA164e73xTdy1CJ3S9o9YS+O21HuiK7T+4uS7FgKDy4w==" + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dependencies": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsv": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/tsv/-/tsv-0.2.0.tgz", + "integrity": "sha512-GG6xbOP85giXXom0dS6z9uyDsxktznjpa1AuDlPrIXDqDnbhjr9Vk6Us8iz6U1nENL4CPS2jZDvIjEdaZsmc4Q==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "peer": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/clickhouse/package.json b/clickhouse/package.json new file mode 100644 index 00000000..75f1fc10 --- /dev/null +++ b/clickhouse/package.json @@ -0,0 +1,17 @@ +{ + "name": "clickhouse", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "ts-node": "^10.9.2" + }, + "dependencies": { + "@pezzo/knex-clickhouse-dialect": "^1.2.0" + } +} diff --git a/clickhouse/tsconfig.json b/clickhouse/tsconfig.json new file mode 100644 index 00000000..3d7f4c9c --- /dev/null +++ b/clickhouse/tsconfig.json @@ -0,0 +1,13 @@ +{ + "ts-node": { + "transpileOnly": true, + "compilerOptions": { + "module": "commonjs" + } + }, + "files": [], + "include": [], + "compilerOptions": { + "esModuleInterop": true, + } +} diff --git a/docker-compose.infra.yaml b/docker-compose.infra.yaml index 33527775..d62f30e4 100644 --- a/docker-compose.infra.yaml +++ b/docker-compose.infra.yaml @@ -1,62 +1,54 @@ version: "3" services: - opensearch-node1: - image: opensearchproject/opensearch:2.5.0 - container_name: opensearch-node1 + pezzo-clickhouse-migrate: + image: ghcr.io/pezzolabs/pezzo/server:latest + build: + context: . + dockerfile: ./apps/server/Dockerfile + entrypoint: /bin/sh + working_dir: /app/clickhouse + command: -c "npx knex migrate:latest" environment: - - cluster.name=opensearch-cluster - - node.name=opensearch-node1 - - discovery.type=single-node - - bootstrap.memory_lock=true - - "plugins.security.disabled=true" - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - volumes: - - opensearch-data1:/usr/share/opensearch/data - healthcheck: - test: - [ - "CMD-SHELL", - "curl --silent --fail localhost:9200/_cluster/health || exit 1", - ] - interval: 20s - timeout: 5s - retries: 10 - ports: - - 9200:9200 # REST API - - 9600:9600 # Performance Analyzer - deploy: - resources: - limits: - memory: 3g + CLICKHOUSE_HOST: clickhouse + CLICKHOUSE_PORT: '8123' + CLICKHOUSE_USER: default + CLICKHOUSE_PASSWORD: default + depends_on: + clickhouse: + condition: service_healthy - opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:2.5.0 - container_name: opensearch-dashboards - ports: - - 5601:5601 + pezzo-prisma-migrate: + image: ghcr.io/pezzolabs/pezzo/server:latest + build: + context: . + dockerfile: ./apps/server/Dockerfile + entrypoint: /bin/sh + command: -c "npx prisma migrate deploy" + environment: + - DATABASE_URL=postgres://postgres:postgres@postgres:5432/pezzo depends_on: - opensearch-node1: + postgres: condition: service_healthy - expose: - - "5601" + + clickhouse: + image: clickhouse/clickhouse-server:23-alpine environment: - - "DISABLE_SECURITY_DASHBOARDS_PLUGIN=true" - - "OPENSEARCH_HOSTS=http://opensearch-node1:9200" + CLICKHOUSE_USER: default + CLICKHOUSE_PASSWORD: default + volumes: + - ./volumes/clickhouse/data:/var/lib/clickhouse + - ./volumes/clickhouse/logs:/var/log/clickhouse-server + - ./clickhouse/config/users.d/config.xml:/etc/clickhouse-server/users.d/config.xml + - ./clickhouse/config/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml + ports: + - "8123:8123" + - "9000:9000" + - "9004:9004" healthcheck: - test: - [ - "CMD-SHELL", - "curl --silent --fail localhost:5601/api/status || exit 1", - ] - interval: 10s + test: ["CMD-SHELL", "clickhouse-client --query \"SELECT 1\""] + interval: 5s timeout: 5s - retries: 20 + retries: 3 postgres: image: postgres:15-alpine3.17 @@ -115,5 +107,4 @@ services: - "KMS_REGION=us-east-1" volumes: - postgres_data: ~ - opensearch-data1: ~ + postgres_data: ~ \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index a9af9bc2..53f0b2b1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,19 +2,6 @@ include: - ./docker-compose.infra.yaml services: - pezzo-server-migrate: - image: ghcr.io/pezzolabs/pezzo/server:latest - build: - context: . - dockerfile: ./apps/server/Dockerfile - entrypoint: /bin/sh - command: -c "npx prisma migrate deploy" - environment: - - DATABASE_URL=postgres://postgres:postgres@postgres:5432/pezzo - depends_on: - postgres: - condition: service_healthy - pezzo-server: image: ghcr.io/pezzolabs/pezzo/server:latest build: @@ -29,7 +16,7 @@ services: environment: - DATABASE_URL=postgres://postgres:postgres@postgres:5432/pezzo - SUPERTOKENS_CONNECTION_URI=http://supertokens:3567 - - OPENSEARCH_URL=http://opensearch-node1:9200 + - CLICKHOUSE_HOST=clickhouse - REDIS_URL=redis://redis-stack-server:6379 - KMS_LOCAL_ENDPOINT=http://local-kms:9981 ports: @@ -41,7 +28,7 @@ services: condition: service_healthy supertokens: condition: service_healthy - opensearch-node1: + clickhouse: condition: service_healthy redis-stack-server: condition: service_healthy diff --git a/libs/common/src/data/models.json b/libs/common/src/data/models.json new file mode 100644 index 00000000..bafde7f8 --- /dev/null +++ b/libs/common/src/data/models.json @@ -0,0 +1,26 @@ +{ + "models": { + "mistralai/Mixtral-8x7B-Instruct-v0.1": { + "by": "mistral", + "providers": [ + { + "provider_id": "anyscale", + "full_model_id": "anyscale/mistralai/Mixtral-8x7B-Instruct-v0.1" + } + ] + }, + "meta-llama/llama-2-7b": { + "by": "meta", + "providers": [ + { + "provider_id": "together_ai", + "full_model_id": "together_ai/togethercomputer/llama-2-7b" + }, + { + "provider_id": "anyscale", + "full_model_id": "anyscale/meta-llama/Llama-2-7b-chat-hf" + } + ] + } + } +} diff --git a/libs/common/src/index.ts b/libs/common/src/index.ts index 4288d783..a89f9aec 100644 --- a/libs/common/src/index.ts +++ b/libs/common/src/index.ts @@ -1,5 +1,4 @@ import * as versionJson from "./version.json"; - -export const version = versionJson.version; - +export * as MetricsTypes from "./metrics/types"; export * from "./utils"; +export const version = versionJson.version; \ No newline at end of file diff --git a/libs/common/src/metrics/types.ts b/libs/common/src/metrics/types.ts new file mode 100644 index 00000000..ee4147d6 --- /dev/null +++ b/libs/common/src/metrics/types.ts @@ -0,0 +1,18 @@ +export type ExeceutionTypeChartResultDataType = { + timestamp: string; + value: number; +}[]; + +export type SuccessErrorRateResultDataType = { + timestamp: string; + success: number; + error: number; + total: number; +}[]; + +export type ModelUsageResultDataType = { + timestamp: string; + model: string; + modelAuthor: string; + value: string; +}[]; \ No newline at end of file diff --git a/libs/common/tsconfig.lib.json b/libs/common/tsconfig.lib.json index 33eca2c2..c79d64f6 100644 --- a/libs/common/tsconfig.lib.json +++ b/libs/common/tsconfig.lib.json @@ -3,7 +3,9 @@ "compilerOptions": { "outDir": "../../dist/out-tsc", "declaration": true, - "types": ["node"] + "types": ["node"], + "resolveJsonModule": true, + "esModuleInterop": true }, "include": ["src/**/*.ts"], "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts index d6cb7a95..5a169eca 100644 --- a/libs/types/src/index.ts +++ b/libs/types/src/index.ts @@ -2,3 +2,4 @@ export * from "./prompt-execution-type"; export * from "./request"; export * from "./ts-helpers"; export * from "./provider.types"; +export * from "./reports"; \ No newline at end of file diff --git a/libs/types/src/reports.ts b/libs/types/src/reports.ts new file mode 100644 index 00000000..de10092e --- /dev/null +++ b/libs/types/src/reports.ts @@ -0,0 +1,86 @@ +export interface ReportSchema { + id: string; + organizationId: string; + projectId: string; + environment: string; + timestamp: string; + promptTokens: number; + completionTokens: number; + totalTokens: number; + promptCost: number; + completionCost: number; + totalCost: number; + duration: number; + type: string; + client: string; + clientVersion: string; + model: string; + modelAuthor: string; + provider: string; + requestTimestamp: string; + requestBody: string; + isError: number; + responseStatusCode: number; + responseTimestamp: string; + responseBody: string; + cacheEnabled: number; + cacheHit: number; + promptId: string; +} + +export interface SerializedReport + extends Omit< + ReportSchema, + "requestBody" | "isError" | "responseBody" | "cacheEnabled" | "cacheHit" + > { + requestBody: Record; + isError: boolean; + responseBody: Record; + cacheEnabled: boolean; + cacheHit: boolean; +} + +export const serializeReport = ( + doc: ReportSchema +): SerializedReport => { + return { + ...doc, + requestBody: JSON.parse(doc.requestBody), + isError: doc.isError === 1, + responseBody: JSON.parse(doc.responseBody), + cacheEnabled: doc.cacheEnabled === 1, + cacheHit: doc.cacheHit === 1, + }; +}; + + +export interface PaginatedReportsSchema { + id: string; + environment: string; + timestamp: string; + totalTokens: number; + totalCost: number; + duration: number; + model: string; + modelAuthor: string; + provider: string; + responseStatusCode: number; + cacheEnabled: number; + cacheHit: number; + promptId: string; +} + +export interface SerializedPaginatedReport extends Omit { + cacheEnabled: boolean; + cacheHit: boolean; +} + +export const serializePaginatedReport = ( + doc: PaginatedReportsSchema +): SerializedPaginatedReport => { + return { + ...doc, + cacheEnabled: doc.cacheEnabled === 1, + cacheHit: doc.cacheHit === 1, + }; +}; diff --git a/libs/types/src/request.ts b/libs/types/src/request.ts index 9a16d7b0..0e0b89c2 100644 --- a/libs/types/src/request.ts +++ b/libs/types/src/request.ts @@ -12,7 +12,9 @@ export type AcceptedModels = ExtractModelNames< export type ObservabilityReportProperties = RecursiveObject; export type ObservabilityReportMetadata = { - provider: Provider; + provider: string; + model: string; + modelAuthor: string; client?: string; clientVersion?: string; environment: string; diff --git a/package-lock.json b/package-lock.json index c8817d9c..87eb70a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@analytics/segment": "^1.1.4", "@apollo/server": "^4.7.0", "@aws-sdk/client-kms": "^3.410.0", - "@aws-sdk/credential-provider-node": "^3.362.0", + "@clickhouse/client": "^0.2.7", "@dqbd/tiktoken": "^1.0.6", "@hookform/resolvers": "^3.3.1", "@nestjs/apollo": "^11.0.6", @@ -27,7 +27,7 @@ "@nestjs/swagger": "^7.1.12", "@nx/nest": "16.6.0", "@nx/next": "16.6.0", - "@opensearch-project/opensearch": "^1.2.0", + "@pezzo/knex-clickhouse-dialect": "^1.2.0", "@pezzo/llm-toolkit": "^0.5.0", "@prisma/client": "^5.1.1", "@radix-ui/react-accordion": "^1.1.2", @@ -78,9 +78,11 @@ "joi": "^17.9.1", "json-stable-stringify": "^1.0.2", "kafkajs": "^2.2.4", + "knex": "^3.1.0", "lottie-react": "^2.4.0", "lucide-react": "^0.274.0", "moment": "^2.29.4", + "mysql2": "^3.6.5", "next": "13.3.0", "openai": "^4.16.1", "pino": "^8.14.1", @@ -3539,6 +3541,22 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@clickhouse/client": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-0.2.7.tgz", + "integrity": "sha512-ZiyarrGngHc+f5AjZSA7mkQfvnE/71jgXk304B0ps8V+aBpE2CsFB6AQmE/Mk2YkP5j+8r/JfG+m0AZWmE27ig==", + "dependencies": { + "@clickhouse/client-common": "0.2.7" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@clickhouse/client-common": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-0.2.7.tgz", + "integrity": "sha512-vgZm+8c5Cu1toIx1/xplF5dEHlCPw+7pJDOOEtLv2CIUVZ0Bl6nGVZ43EWxRdHeah9ivTfoRWhN1zI1PxjH0xQ==" + }, "node_modules/@codemirror/autocomplete": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz", @@ -11001,21 +11019,6 @@ "@octokit/openapi-types": "^12.11.0" } }, - "node_modules/@opensearch-project/opensearch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-1.2.0.tgz", - "integrity": "sha512-bX0aUz5e7rlY1lKz1rFrqnNbl/l1CHvvysYB2Jn+C3WNs7nL6FnQjuxLhGwyRdW9W1bFokDoOVgPMIOi/Nn9/g==", - "dependencies": { - "aws4": "^1.11.0", - "debug": "^4.3.1", - "hpagent": "^0.1.1", - "ms": "^2.1.3", - "secure-json-parse": "^2.4.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@parcel/watcher": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.2.0.tgz", @@ -11287,6 +11290,85 @@ "node": ">=10.12.0" } }, + "node_modules/@pezzo/knex-clickhouse-dialect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", + "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "dependencies": { + "clickhouse": "^2.6.0", + "knex": "^2.5.1", + "lodash": "^4.17.21", + "sqlstring": "^2.3.3" + } + }, + "node_modules/@pezzo/knex-clickhouse-dialect/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/@pezzo/knex-clickhouse-dialect/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pezzo/knex-clickhouse-dialect/node_modules/knex": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", + "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.1", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/@pezzo/knex-clickhouse-dialect/node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, "node_modules/@pezzo/llm-toolkit": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@pezzo/llm-toolkit/-/llm-toolkit-0.5.0.tgz", @@ -21575,6 +21657,14 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -21602,6 +21692,14 @@ "util": "^0.12.5" } }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", @@ -21740,6 +21838,14 @@ "node": ">=10" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, "node_modules/aws4": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", @@ -22192,6 +22298,14 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -22927,6 +23041,11 @@ "node": ">=4" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -23244,6 +23363,30 @@ "node": ">= 10" } }, + "node_modules/clickhouse": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/clickhouse/-/clickhouse-2.6.0.tgz", + "integrity": "sha512-HC5OV99GJOup4qZsTuWWPpXlj+847Z0OeygDU2x22rNYost0V/vWapzFWYZdV/5iRbGMrhFQPOyQEzmGvoaWRQ==", + "dependencies": { + "JSONStream": "1.3.4", + "lodash": "4.17.21", + "querystring": "0.2.0", + "request": "2.88.0", + "stream2asynciter": "1.0.3", + "through": "2.3.8", + "tsv": "0.2.0", + "uuid": "3.4.0" + } + }, + "node_modules/clickhouse/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -24449,6 +24592,17 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -24957,6 +25111,14 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -25369,6 +25531,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -26487,6 +26658,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -26947,6 +27126,14 @@ "url": "https://github.com/sponsors/jaydenseric" } }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-copy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", @@ -27618,6 +27805,14 @@ "node": ">=8.0.0" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "7.2.13", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", @@ -28102,6 +28297,14 @@ "node": ">=12" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generate-package-json-webpack-plugin": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/generate-package-json-webpack-plugin/-/generate-package-json-webpack-plugin-2.6.0.tgz", @@ -28223,6 +28426,19 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -28775,6 +28991,47 @@ "node": ">=0.10.0" } }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -29009,11 +29266,6 @@ "wbuf": "^1.1.0" } }, - "node_modules/hpagent": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-0.1.2.tgz", - "integrity": "sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==" - }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -29386,6 +29638,20 @@ "node": ">=8" } }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/http2-wrapper": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", @@ -29817,6 +30083,14 @@ "node": ">=12" } }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/intl-tel-input": { "version": "17.0.21", "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.21.tgz", @@ -30503,6 +30777,11 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -30629,8 +30908,7 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "node_modules/is-unc-path": { "version": "1.0.0", @@ -30753,6 +31031,11 @@ "ws": "*" } }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -34920,6 +35203,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -35060,6 +35348,11 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -35082,6 +35375,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "devOptional": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", @@ -35183,6 +35481,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", + "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", @@ -35242,6 +35563,20 @@ "node": ">=10" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -35344,6 +35679,69 @@ "node": ">= 8" } }, + "node_modules/knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/knex/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -37566,6 +37964,48 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mysql2": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", + "integrity": "sha512-pS/KqIb0xlXmtmqEuTvBXTmLoQ5LmAz5NW/r8UyQ1ldvnprNEj3P9GbmuQQ2J0A4LO+ynotGi6TbscPa8OUb+w==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mysql2/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -37576,6 +38016,17 @@ "thenify-all": "^1.0.0" } }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -38564,6 +39015,14 @@ "node": ">=6" } }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -39464,6 +39923,16 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -41206,8 +41675,7 @@ "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "node_modules/pupa": { "version": "2.1.1", @@ -41276,6 +41744,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -42664,6 +43141,17 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -43146,6 +43634,79 @@ "entities": "^2.0.0" } }, + "node_modules/request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dependencies": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -44744,6 +45305,11 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -45444,6 +46010,38 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -45547,6 +46145,11 @@ "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==", "dev": true }, + "node_modules/stream2asynciter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream2asynciter/-/stream2asynciter-1.0.3.tgz", + "integrity": "sha512-9/dEZW+LQjuW6ub5hmWi4n9Pn8W8qA8k7NAE1isecesA164e73xTdy1CJ3S9o9YS+O21HuiK7T+4uS7FgKDy4w==" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -46553,6 +47156,14 @@ "node": ">=10" } }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/telejson": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", @@ -46802,6 +47413,14 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "engines": { + "node": ">=8" + } + }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -47390,6 +48009,11 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tsv": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/tsv/-/tsv-0.2.0.tgz", + "integrity": "sha512-GG6xbOP85giXXom0dS6z9uyDsxktznjpa1AuDlPrIXDqDnbhjr9Vk6Us8iz6U1nENL4CPS2jZDvIjEdaZsmc4Q==" + }, "node_modules/tsx": { "version": "3.12.7", "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.7.tgz", @@ -47420,7 +48044,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -47428,6 +48051,11 @@ "node": "*" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/twilio": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/twilio/-/twilio-4.14.1.tgz", @@ -48292,6 +48920,24 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, "node_modules/vfile": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", @@ -51891,6 +52537,19 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "@clickhouse/client": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-0.2.7.tgz", + "integrity": "sha512-ZiyarrGngHc+f5AjZSA7mkQfvnE/71jgXk304B0ps8V+aBpE2CsFB6AQmE/Mk2YkP5j+8r/JfG+m0AZWmE27ig==", + "requires": { + "@clickhouse/client-common": "0.2.7" + } + }, + "@clickhouse/client-common": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-0.2.7.tgz", + "integrity": "sha512-vgZm+8c5Cu1toIx1/xplF5dEHlCPw+7pJDOOEtLv2CIUVZ0Bl6nGVZ43EWxRdHeah9ivTfoRWhN1zI1PxjH0xQ==" + }, "@codemirror/autocomplete": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz", @@ -57246,18 +57905,6 @@ "@octokit/openapi-types": "^12.11.0" } }, - "@opensearch-project/opensearch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-1.2.0.tgz", - "integrity": "sha512-bX0aUz5e7rlY1lKz1rFrqnNbl/l1CHvvysYB2Jn+C3WNs7nL6FnQjuxLhGwyRdW9W1bFokDoOVgPMIOi/Nn9/g==", - "requires": { - "aws4": "^1.11.0", - "debug": "^4.3.1", - "hpagent": "^0.1.1", - "ms": "^2.1.3", - "secure-json-parse": "^2.4.0" - } - }, "@parcel/watcher": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.2.0.tgz", @@ -57383,6 +58030,55 @@ "webcrypto-core": "^1.7.7" } }, + "@pezzo/knex-clickhouse-dialect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", + "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "requires": { + "clickhouse": "^2.6.0", + "knex": "^2.5.1", + "lodash": "^4.17.21", + "sqlstring": "^2.3.3" + }, + "dependencies": { + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, + "knex": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", + "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "requires": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.1", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + } + }, + "pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + } + } + }, "@pezzo/llm-toolkit": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@pezzo/llm-toolkit/-/llm-toolkit-0.5.0.tgz", @@ -64620,6 +65316,14 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, "asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -64644,6 +65348,11 @@ "util": "^0.12.5" } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, "ast-types": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", @@ -64736,6 +65445,11 @@ "is-promise": "^4.0.0" } }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, "aws4": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", @@ -65095,6 +65809,14 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, "before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -65635,6 +66357,11 @@ "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -65863,6 +66590,28 @@ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, + "clickhouse": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/clickhouse/-/clickhouse-2.6.0.tgz", + "integrity": "sha512-HC5OV99GJOup4qZsTuWWPpXlj+847Z0OeygDU2x22rNYost0V/vWapzFWYZdV/5iRbGMrhFQPOyQEzmGvoaWRQ==", + "requires": { + "JSONStream": "1.3.4", + "lodash": "4.17.21", + "querystring": "0.2.0", + "request": "2.88.0", + "stream2asynciter": "1.0.3", + "through": "2.3.8", + "tsv": "0.2.0", + "uuid": "3.4.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -66783,6 +67532,14 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, "data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -67133,6 +67890,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -67442,6 +68204,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -68292,6 +69063,11 @@ "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", "devOptional": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -68655,6 +69431,11 @@ "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", "dev": true }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, "fast-copy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", @@ -69167,6 +69948,11 @@ "signal-exit": "^3.0.2" } }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, "fork-ts-checker-webpack-plugin": { "version": "7.2.13", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", @@ -69505,6 +70291,14 @@ "json-bigint": "^1.0.0" } }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, "generate-package-json-webpack-plugin": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/generate-package-json-webpack-plugin/-/generate-package-json-webpack-plugin-2.6.0.tgz", @@ -69595,6 +70389,19 @@ "resolve-pkg-maps": "^1.0.0" } }, + "getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + } + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -70017,6 +70824,38 @@ } } }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } + } + }, "harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -70192,11 +71031,6 @@ "wbuf": "^1.1.0" } }, - "hpagent": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-0.1.2.tgz", - "integrity": "sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==" - }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -70476,6 +71310,16 @@ } } }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, "http2-wrapper": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", @@ -70782,6 +71626,11 @@ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" + }, "intl-tel-input": { "version": "17.0.21", "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.21.tgz", @@ -71237,6 +72086,11 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -71321,8 +72175,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "is-unc-path": { "version": "1.0.0", @@ -71416,6 +72269,11 @@ "dev": true, "requires": {} }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -74571,6 +75429,11 @@ "argparse": "^2.0.1" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -74681,6 +75544,11 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -74700,6 +75568,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "devOptional": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "json-to-pretty-yaml": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", @@ -74774,6 +75647,20 @@ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==" }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==" + }, + "JSONStream": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", + "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "jsonwebtoken": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", @@ -74822,6 +75709,17 @@ } } }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -74901,6 +75799,39 @@ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true }, + "knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "requires": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "dependencies": { + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + } + } + }, "language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -76469,6 +77400,41 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "mysql2": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", + "integrity": "sha512-pS/KqIb0xlXmtmqEuTvBXTmLoQ5LmAz5NW/r8UyQ1ldvnprNEj3P9GbmuQQ2J0A4LO+ynotGi6TbscPa8OUb+w==", + "requires": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==" + } + } + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -76479,6 +77445,14 @@ "thenify-all": "^1.0.0" } }, + "named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "requires": { + "lru-cache": "^7.14.1" + } + }, "nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -77201,6 +78175,11 @@ } } }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -77845,6 +78824,16 @@ "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", "dev": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -79005,8 +79994,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "pupa": { "version": "2.1.1", @@ -79051,6 +80039,11 @@ "side-channel": "^1.0.4" } }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -80013,6 +81006,14 @@ "decimal.js-light": "^2.4.1" } }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "requires": { + "resolve": "^1.20.0" + } + }, "redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -80392,6 +81393,64 @@ } } }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -81512,6 +82571,11 @@ "upper-case-first": "^2.0.2" } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -82077,6 +83141,27 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" + }, + "sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -82162,6 +83247,11 @@ "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==", "dev": true }, + "stream2asynciter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream2asynciter/-/stream2asynciter-1.0.3.tgz", + "integrity": "sha512-9/dEZW+LQjuW6ub5hmWi4n9Pn8W8qA8k7NAE1isecesA164e73xTdy1CJ3S9o9YS+O21HuiK7T+4uS7FgKDy4w==" + }, "streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -82899,6 +83989,11 @@ } } }, + "tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==" + }, "telejson": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", @@ -83076,6 +84171,11 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, "tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -83492,6 +84592,11 @@ } } }, + "tsv": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/tsv/-/tsv-0.2.0.tgz", + "integrity": "sha512-GG6xbOP85giXXom0dS6z9uyDsxktznjpa1AuDlPrIXDqDnbhjr9Vk6Us8iz6U1nENL4CPS2jZDvIjEdaZsmc4Q==" + }, "tsx": { "version": "3.12.7", "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.7.tgz", @@ -83514,11 +84619,15 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "twilio": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/twilio/-/twilio-4.14.1.tgz", @@ -84123,6 +85232,23 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + } + } + }, "vfile": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", diff --git a/package.json b/package.json index 72f2ca73..dcc150ae 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@analytics/segment": "^1.1.4", "@apollo/server": "^4.7.0", "@aws-sdk/client-kms": "^3.410.0", - "@aws-sdk/credential-provider-node": "^3.362.0", + "@clickhouse/client": "^0.2.7", "@dqbd/tiktoken": "^1.0.6", "@hookform/resolvers": "^3.3.1", "@nestjs/apollo": "^11.0.6", @@ -28,7 +28,7 @@ "@nestjs/swagger": "^7.1.12", "@nx/nest": "16.6.0", "@nx/next": "16.6.0", - "@opensearch-project/opensearch": "^1.2.0", + "@pezzo/knex-clickhouse-dialect": "^1.2.0", "@pezzo/llm-toolkit": "^0.5.0", "@prisma/client": "^5.1.1", "@radix-ui/react-accordion": "^1.1.2", @@ -79,9 +79,11 @@ "joi": "^17.9.1", "json-stable-stringify": "^1.0.2", "kafkajs": "^2.2.4", + "knex": "^3.1.0", "lottie-react": "^2.4.0", "lucide-react": "^0.274.0", "moment": "^2.29.4", + "mysql2": "^3.6.5", "next": "13.3.0", "openai": "^4.16.1", "pino": "^8.14.1", diff --git a/tsconfigz.json b/tsconfigz.json new file mode 100644 index 00000000..ffcbb947 --- /dev/null +++ b/tsconfigz.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.base.json" +} From f35572edf1ec8678e10c25df2496c9976642665c Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 02:31:33 -0800 Subject: [PATCH 2/8] fix formatting --- .nxignore | 3 +- .prettierignore | 1 + .../src/components/metrics/StatisticBox.tsx | 2 +- .../editor/ProviderSelector/providers.tsx | 14 +---- .../requests/RequestResponseViewJsonView.tsx | 5 +- .../graphql/definitions/queries/metrics.tsx | 14 ++--- apps/console/src/graphql/hooks/queries.ts | 14 ++--- apps/console/src/graphql/types.ts | 6 +-- .../pages/projects/overview/DashboardPage.tsx | 2 +- .../overview/charts/ExecutionTimeChart.tsx | 41 +++++++------- .../overview/charts/SuccessErrorRateChart.tsx | 49 +++++++++-------- .../src/pages/prompts/PromptNavigation.tsx | 2 +- .../pages/requests/model-display-details.tsx | 12 ++--- apps/server/.dockerignore | 3 +- .../src/app/clickhouse/clickhouse.service.ts | 2 +- .../src/app/common/filters/filter.input.ts | 6 ++- .../inputs/get-project-metrics.input.ts | 8 ++- apps/server/src/app/metrics/metrics.module.ts | 6 +-- .../metrics/models/project-metric.model.ts | 6 +-- .../app/metrics/project-metrics.resolver.ts | 5 +- .../app/metrics/project-metrics.service.ts | 10 ++-- .../queries/average-request-duration-query.ts | 13 +++-- .../app/metrics/queries/model-usage-query.ts | 15 ++++-- .../queries/success-error-rate-query.ts | 24 ++++++--- .../app/reporting/dto/create-report.dto.ts | 6 +-- .../request-report-result.model.ts | 2 +- .../src/app/reporting/reporting.service.ts | 4 +- .../src/app/reporting/requests.resolver.ts | 7 ++- .../20231231065935_create_reports.ts | 54 +++++++++---------- ...20231231100122_create_report_properties.ts | 34 ++++++------ clickhouse/tsconfig.json | 2 +- docker-compose.infra.yaml | 6 +-- libs/common/src/index.ts | 2 +- libs/common/src/metrics/types.ts | 4 +- libs/types/src/index.ts | 2 +- libs/types/src/reports.ts | 8 ++- tsconfigz.json | 3 -- 37 files changed, 209 insertions(+), 188 deletions(-) delete mode 100644 tsconfigz.json diff --git a/.nxignore b/.nxignore index e32b9507..a8393195 100644 --- a/.nxignore +++ b/.nxignore @@ -1,2 +1,3 @@ .github -.next \ No newline at end of file +.next +volumes \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index a72ae4fa..264f07c8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ libs/common/src/version.json **/.next /docs +volumes \ No newline at end of file diff --git a/apps/console/src/components/metrics/StatisticBox.tsx b/apps/console/src/components/metrics/StatisticBox.tsx index e8d0e439..1a471993 100644 --- a/apps/console/src/components/metrics/StatisticBox.tsx +++ b/apps/console/src/components/metrics/StatisticBox.tsx @@ -26,7 +26,7 @@ interface Props { const getProperties = ( currentValue: number, previousValue: number, - reverseColors: boolean, + reverseColors: boolean ) => { const diff = currentValue - previousValue; // Handle case where previousValue is 0 diff --git a/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx b/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx index f9ec0bd6..43956433 100644 --- a/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx +++ b/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx @@ -9,13 +9,7 @@ import AnthropicLogo from "~/assets/providers/anthropic-logo.png"; export const providersList: ProviderProps[] = [ { - image: ( - OpenAI - ), + image: OpenAI, value: PromptService.OpenAiChatCompletion, label: promptProvidersMapping[PromptService.OpenAiChatCompletion].name, }, @@ -32,11 +26,7 @@ export const providersList: ProviderProps[] = [ }, { image: ( - Anthropic + Anthropic ), value: PromptService.AnthropicCompletion, label: promptProvidersMapping[PromptService.AnthropicCompletion].name, diff --git a/apps/console/src/components/requests/RequestResponseViewJsonView.tsx b/apps/console/src/components/requests/RequestResponseViewJsonView.tsx index 15edbd86..43bbef7b 100644 --- a/apps/console/src/components/requests/RequestResponseViewJsonView.tsx +++ b/apps/console/src/components/requests/RequestResponseViewJsonView.tsx @@ -6,7 +6,10 @@ interface Props { responseBody: OpenAI.ChatCompletion; } -export const RequestResponseViewJsonView = ({ requestBody, responseBody }: Props) => { +export const RequestResponseViewJsonView = ({ + requestBody, + responseBody, +}: Props) => { return (
diff --git a/apps/console/src/graphql/definitions/queries/metrics.tsx b/apps/console/src/graphql/definitions/queries/metrics.tsx index 4f920d1d..fbb580a2 100644 --- a/apps/console/src/graphql/definitions/queries/metrics.tsx +++ b/apps/console/src/graphql/definitions/queries/metrics.tsx @@ -1,7 +1,9 @@ import { graphql } from "~/@generated/graphql"; export const GET_GENERIC_PROJECT_METRIC_HISTOGRAM = graphql(/* GraphQL */ ` - query getGenericProjectMetricHistogram($data: GetProjectGenericHistogramInput!) { + query getGenericProjectMetricHistogram( + $data: GetProjectGenericHistogramInput! + ) { genericProjectMetricHistogram(data: $data) { data } @@ -9,10 +11,10 @@ export const GET_GENERIC_PROJECT_METRIC_HISTOGRAM = graphql(/* GraphQL */ ` `); export const GET_PROJECT_METRIC_DELTA = graphql(/* GraphQL */ ` -query getProjectMetricDelta($data: GetProjectMetricDeltaInput!) { - projectMetricDelta(data: $data) { - currentValue, - previousValue + query getProjectMetricDelta($data: GetProjectMetricDeltaInput!) { + projectMetricDelta(data: $data) { + currentValue + previousValue + } } -} `); diff --git a/apps/console/src/graphql/hooks/queries.ts b/apps/console/src/graphql/hooks/queries.ts index b4bc1a6f..3bb2897e 100644 --- a/apps/console/src/graphql/hooks/queries.ts +++ b/apps/console/src/graphql/hooks/queries.ts @@ -18,7 +18,7 @@ import { GetProjectMetricDeltaQueryVariables, GetProjectMetricDeltaQuery, GetReportQuery, - GetReportQueryVariables + GetReportQueryVariables, } from "~/@generated/graphql/graphql"; import { GraphQLErrorResponse } from "../types"; import { GET_PROMPT, GET_PROMPT_VERSION } from "../definitions/queries/prompts"; @@ -141,10 +141,7 @@ export const useGetRequestReports = ( export const useReport = ( data: GetReportQueryVariables["data"], - options: UseQueryOptions< - GetReportQuery, - GraphQLErrorResponse - > = {} + options: UseQueryOptions = {} ) => { const result = useQuery({ queryKey: ["report", data.reportId], @@ -155,7 +152,7 @@ export const useReport = ( ...options, }); - return { ...result, report: result.data?.report as SerializedReport}; + return { ...result, report: result.data?.report as SerializedReport }; }; export const useGenericProjectMetricHistogram = ( @@ -174,7 +171,10 @@ export const useGenericProjectMetricHistogram = ( ...options, }); - return { ...result, histogram: result.data?.genericProjectMetricHistogram as { data: T }}; + return { + ...result, + histogram: result.data?.genericProjectMetricHistogram as { data: T }, + }; }; export const useProjctMetricDelta = ( diff --git a/apps/console/src/graphql/types.ts b/apps/console/src/graphql/types.ts index 5ae78cf9..240e8cdc 100644 --- a/apps/console/src/graphql/types.ts +++ b/apps/console/src/graphql/types.ts @@ -1,6 +1,4 @@ -import { - Provider, -} from "@pezzo/types"; +import { Provider } from "@pezzo/types"; import { GraphQLError } from "graphql-request/build/esm/types"; export interface GraphQLErrorResponse { @@ -13,4 +11,4 @@ export interface GraphQLErrorResponse { export type ReportRequestResponse< TProviderType extends Provider | unknown = unknown -> = Record; \ No newline at end of file +> = Record; diff --git a/apps/console/src/pages/projects/overview/DashboardPage.tsx b/apps/console/src/pages/projects/overview/DashboardPage.tsx index d88ab04b..8cb4c364 100644 --- a/apps/console/src/pages/projects/overview/DashboardPage.tsx +++ b/apps/console/src/pages/projects/overview/DashboardPage.tsx @@ -36,7 +36,7 @@ export const DashboardPage = () => {
-
+
diff --git a/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx b/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx index 869fc6f5..01f95465 100644 --- a/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx +++ b/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx @@ -14,9 +14,7 @@ import { useProjectMetricControls } from "./ProjectMetricContext"; import { TooltipWithTimestamp } from "./TooltipWithTimestamp"; import { useCurrentProject } from "~/lib/hooks/useCurrentProject"; import { useGenericProjectMetricHistogram } from "~/graphql/hooks/queries"; -import { - HistogramIdType, -} from "~/@generated/graphql/graphql"; +import { HistogramIdType } from "~/@generated/graphql/graphql"; import { useFiltersAndSortParams } from "~/lib/hooks/useFiltersAndSortParams"; import { Loader2Icon } from "lucide-react"; import { MetricsTypes } from "@pezzo/common"; @@ -27,19 +25,20 @@ export const ExecutionTimeChart = () => { const controls = useProjectMetricControls(); const { filters } = useFiltersAndSortParams(); - const durationHistogram = useGenericProjectMetricHistogram( - { - projectId: project?.id, - histogramId: HistogramIdType.RequestDuration, - bucketSize: controls.bucketSize, - startDate: startDate, - endDate: endDate, - filters, - }, - { - enabled: !!project && !!startDate && !!endDate, - } - ); + const durationHistogram = + useGenericProjectMetricHistogram( + { + projectId: project?.id, + histogramId: HistogramIdType.RequestDuration, + bucketSize: controls.bucketSize, + startDate: startDate, + endDate: endDate, + filters, + }, + { + enabled: !!project && !!startDate && !!endDate, + } + ); if (durationHistogram.isLoading) { return ( @@ -58,8 +57,10 @@ export const ExecutionTimeChart = () => { ); } - - const data = durationHistogram.histogram.data.map((d) => ({ timestamp: d.timestamp, value: d.value })); + const data = durationHistogram.histogram.data.map((d) => ({ + timestamp: d.timestamp, + value: d.value, + })); return ( @@ -85,7 +86,9 @@ export const ExecutionTimeChart = () => { cursor={{ opacity: 0.2 }} content={TooltipWithTimestamp} formatter={(value, key) => - key !== "value" ? value : `${((value as number) / 1000).toFixed(2)}s` + key !== "value" + ? value + : `${((value as number) / 1000).toFixed(2)}s` } /> { const { filters } = useFiltersAndSortParams(); const { project } = useCurrentProject(); - const histogram = useGenericProjectMetricHistogram( - { - projectId: project?.id, - histogramId: HistogramIdType.SuccessErrorRate, - bucketSize: controls.bucketSize, - startDate: startDate, - endDate: endDate, - filters, - }, - { - enabled: !!project && !!startDate && !!endDate, - } - ); + const histogram = + useGenericProjectMetricHistogram( + { + projectId: project?.id, + histogramId: HistogramIdType.SuccessErrorRate, + bucketSize: controls.bucketSize, + startDate: startDate, + endDate: endDate, + filters, + }, + { + enabled: !!project && !!startDate && !!endDate, + } + ); - if ( - histogram.isLoading - ) { + if (histogram.isLoading) { return (
{ }, ]} /> - - + + ); diff --git a/apps/console/src/pages/prompts/PromptNavigation.tsx b/apps/console/src/pages/prompts/PromptNavigation.tsx index ccce311c..f467886b 100644 --- a/apps/console/src/pages/prompts/PromptNavigation.tsx +++ b/apps/console/src/pages/prompts/PromptNavigation.tsx @@ -39,7 +39,7 @@ export const PromptNavigation = () => { icon: GitCommitIcon, href: `${basePath}/versions`, isActive: (href) => window.location.pathname === href, - } + }, ]; return ( diff --git a/apps/console/src/pages/requests/model-display-details.tsx b/apps/console/src/pages/requests/model-display-details.tsx index 2a811b0a..7759c0c3 100644 --- a/apps/console/src/pages/requests/model-display-details.tsx +++ b/apps/console/src/pages/requests/model-display-details.tsx @@ -6,17 +6,17 @@ export const modelAuthorDetails = { openai: { image: OpenAILogo, name: "OpenAI", - color: "#3B976B" + color: "#3B976B", }, mistral: { image: MistralLogo, name: "Mistral", - color: "#cf651f" + color: "#cf651f", }, meta: { image: MetaLogo, name: "Meta", - color: "#579BE0" + color: "#579BE0", }, }; @@ -26,9 +26,9 @@ export const getModelDisplayDetails = (modelAuthor: string) => { if (!authorDetails) { return { image: , - name: "Unknown" - } + name: "Unknown", + }; } return authorDetails; -} \ No newline at end of file +}; diff --git a/apps/server/.dockerignore b/apps/server/.dockerignore index bbe63a07..8f691507 100644 --- a/apps/server/.dockerignore +++ b/apps/server/.dockerignore @@ -1,3 +1,4 @@ .env .env* -project.json \ No newline at end of file +project.json +volumes \ No newline at end of file diff --git a/apps/server/src/app/clickhouse/clickhouse.service.ts b/apps/server/src/app/clickhouse/clickhouse.service.ts index 906df498..693bfc67 100644 --- a/apps/server/src/app/clickhouse/clickhouse.service.ts +++ b/apps/server/src/app/clickhouse/clickhouse.service.ts @@ -45,7 +45,7 @@ export class ClickHouseService implements OnModuleInit { user: username, password, database, - timezone: "Z" + timezone: "Z", }, }); diff --git a/apps/server/src/app/common/filters/filter.input.ts b/apps/server/src/app/common/filters/filter.input.ts index c01fd769..780b573f 100644 --- a/apps/server/src/app/common/filters/filter.input.ts +++ b/apps/server/src/app/common/filters/filter.input.ts @@ -16,7 +16,9 @@ registerEnumType(FilterOperator, { name: "FilterOperator", }); -export const getSQLOperatorByFilterOperator = (operator: FilterOperator): string => { +export const getSQLOperatorByFilterOperator = ( + operator: FilterOperator +): string => { switch (operator) { case FilterOperator.eq: return "="; @@ -39,7 +41,7 @@ export const getSQLOperatorByFilterOperator = (operator: FilterOperator): string default: throw new Error(`Unknown filter operator: ${operator}`); } -} +}; @InputType() export class FilterInput { diff --git a/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts b/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts index 815a3e98..dba0aeaf 100644 --- a/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts +++ b/apps/server/src/app/metrics/inputs/get-project-metrics.input.ts @@ -7,7 +7,7 @@ export enum ProjectMetricType { duration = "duration", successfulRequests = "successfulRequests", erroneousRequests = "erroneousRequests", - model = "model" + model = "model", } registerEnumType(ProjectMetricType, { @@ -104,7 +104,6 @@ export class GetProjectMetricHistogramInput extends BaseProjectMetricInput { metric: ProjectMetricType; } - @InputType() export class GetProjectGenericHistogramInput extends BaseProjectMetricInput { @Field(() => HistogramIdType, { nullable: false }) @@ -112,8 +111,7 @@ export class GetProjectGenericHistogramInput extends BaseProjectMetricInput { } @InputType() -export class GetProjectModelUsageHistogramInput extends BaseProjectMetricInput { -} +export class GetProjectModelUsageHistogramInput extends BaseProjectMetricInput {} @InputType() export class GetProjectMetricDeltaInput { @@ -128,4 +126,4 @@ export class GetProjectMetricDeltaInput { @Field(() => DeltaMetricType, { nullable: true }) metric: DeltaMetricType; -} \ No newline at end of file +} diff --git a/apps/server/src/app/metrics/metrics.module.ts b/apps/server/src/app/metrics/metrics.module.ts index 6d322e64..9c890331 100644 --- a/apps/server/src/app/metrics/metrics.module.ts +++ b/apps/server/src/app/metrics/metrics.module.ts @@ -8,10 +8,6 @@ import { ClickhHouseModule } from "../clickhouse/clickhouse.module"; @Module({ imports: [IdentityModule, PromptsModule, ClickhHouseModule], - providers: [ - ProjectMetricsResolver, - PrismaService, - ProjectMetricsService, - ], + providers: [ProjectMetricsResolver, PrismaService, ProjectMetricsService], }) export class MetricsModule {} diff --git a/apps/server/src/app/metrics/models/project-metric.model.ts b/apps/server/src/app/metrics/models/project-metric.model.ts index dc888d1b..8fb86d5a 100644 --- a/apps/server/src/app/metrics/models/project-metric.model.ts +++ b/apps/server/src/app/metrics/models/project-metric.model.ts @@ -27,7 +27,7 @@ export class ModelUsageHistogramValue { @Field(() => String, { nullable: false }) modelAuthor: string; - + @Field(() => Number, { nullable: false }) value: number; } @@ -36,7 +36,7 @@ export class ModelUsageHistogramValue { export class ModelUsageHistogramBucket { @Field(() => String, { nullable: false }) timestamp: string; - + @Field(() => [ModelUsageHistogramValue], { nullable: false }) values: ModelUsageHistogramValue[]; // Value for the metric at that date } @@ -54,4 +54,4 @@ export class ProjectMetricDeltaResult { @Field(() => Number, { nullable: false }) previousValue: number; -} \ No newline at end of file +} diff --git a/apps/server/src/app/metrics/project-metrics.resolver.ts b/apps/server/src/app/metrics/project-metrics.resolver.ts index 63b81265..d85e1d54 100644 --- a/apps/server/src/app/metrics/project-metrics.resolver.ts +++ b/apps/server/src/app/metrics/project-metrics.resolver.ts @@ -34,7 +34,8 @@ export class ProjectMetricsResolver { this.logger.assign({ data }); this.logger.info("Getting project metric histogram"); - const { projectId, histogramId, startDate, endDate, bucketSize, filters } = data; + const { projectId, histogramId, startDate, endDate, bucketSize, filters } = + data; const project = await this.prismaService.project.findUnique({ where: { @@ -75,7 +76,7 @@ export class ProjectMetricsResolver { projectId, startDate, endDate, - metric, + metric ); return result; diff --git a/apps/server/src/app/metrics/project-metrics.service.ts b/apps/server/src/app/metrics/project-metrics.service.ts index fd3fd94b..253f4b36 100644 --- a/apps/server/src/app/metrics/project-metrics.service.ts +++ b/apps/server/src/app/metrics/project-metrics.service.ts @@ -1,4 +1,8 @@ -import { BadRequestException, Injectable, InternalServerErrorException } from "@nestjs/common"; +import { + BadRequestException, + Injectable, + InternalServerErrorException, +} from "@nestjs/common"; import { DeltaMetricType, HistogramIdType, @@ -16,9 +20,7 @@ import { successErrorRateQuery } from "./queries/success-error-rate-query"; @Injectable() export class ProjectMetricsService { - constructor( - private clickHouseService: ClickHouseService - ) {} + constructor(private clickHouseService: ClickHouseService) {} async getGenericHistogram( projectId: string, diff --git a/apps/server/src/app/metrics/queries/average-request-duration-query.ts b/apps/server/src/app/metrics/queries/average-request-duration-query.ts index 6b5178b2..87b61541 100644 --- a/apps/server/src/app/metrics/queries/average-request-duration-query.ts +++ b/apps/server/src/app/metrics/queries/average-request-duration-query.ts @@ -1,5 +1,8 @@ import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; -import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { + generateBucketsSubquery, + timePropertiesFromBucketSize, +} from "./queries.utils"; import { Knex } from "knex"; export const averageRequestDurationQuery = ( @@ -28,10 +31,14 @@ export const averageRequestDurationQuery = ( }) .from("buckets as b") .leftJoin("reports as r", (join) => - join.on(knex.raw(`${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'`)) + join.on( + knex.raw( + `${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'` + ) + ) ) .groupBy("b.timestamp") .orderBy("b.timestamp"); return mainquery; -}; \ No newline at end of file +}; diff --git a/apps/server/src/app/metrics/queries/model-usage-query.ts b/apps/server/src/app/metrics/queries/model-usage-query.ts index c462487e..61dcc7d7 100644 --- a/apps/server/src/app/metrics/queries/model-usage-query.ts +++ b/apps/server/src/app/metrics/queries/model-usage-query.ts @@ -1,5 +1,8 @@ import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; -import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { + generateBucketsSubquery, + timePropertiesFromBucketSize, +} from "./queries.utils"; import { Knex } from "knex"; export const modelUsageQuery = ( @@ -30,14 +33,18 @@ export const modelUsageQuery = ( NULL, toJSONString(arrayReduce('sumMap', arrayMap((model, duration) -> map(model, duration), groupArray(r.model), groupArray(r.duration)))) ) - `) + `), }) .from(bucketsSubquery) .leftJoin("reports as r", (join) => - join.on(knex.raw(`${timeProps.roundFn}(r."request.timestamp") = s.timeframe AND r."projectId" = '${projectId}'`)) + join.on( + knex.raw( + `${timeProps.roundFn}(r."request.timestamp") = s.timeframe AND r."projectId" = '${projectId}'` + ) + ) ) .groupBy(["s.timeframe"]) - .orderBy(["s.timeframe"]) + .orderBy(["s.timeframe"]); return mainquery; }; diff --git a/apps/server/src/app/metrics/queries/success-error-rate-query.ts b/apps/server/src/app/metrics/queries/success-error-rate-query.ts index 19b4f238..d881686a 100644 --- a/apps/server/src/app/metrics/queries/success-error-rate-query.ts +++ b/apps/server/src/app/metrics/queries/success-error-rate-query.ts @@ -1,5 +1,8 @@ import { ProjectMetricHistogramBucketSize } from "../inputs/get-project-metrics.input"; -import { generateBucketsSubquery, timePropertiesFromBucketSize } from "./queries.utils"; +import { + generateBucketsSubquery, + timePropertiesFromBucketSize, +} from "./queries.utils"; import { Knex } from "knex"; export const successErrorRateQuery = ( @@ -21,19 +24,24 @@ export const successErrorRateQuery = ( }); const mainquery = knex - .with( - "buckets", - bucketsSubquery - ) + .with("buckets", bucketsSubquery) .select({ timestamp: "b.timestamp", requests: knex.raw("count(*)"), - error: knex.raw("countIf(r.isError = true AND toStartOfHour(r.requestTimestamp) = timestamp)"), - success: knex.raw("countIf(r.isError = false AND toStartOfHour(r.requestTimestamp) = timestamp)"), + error: knex.raw( + "countIf(r.isError = true AND toStartOfHour(r.requestTimestamp) = timestamp)" + ), + success: knex.raw( + "countIf(r.isError = false AND toStartOfHour(r.requestTimestamp) = timestamp)" + ), }) .from("buckets as b") .leftJoin("reports as r", (join) => - join.on(knex.raw(`${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'`)) + join.on( + knex.raw( + `${timeProps.roundFn}(r.requestTimestamp) = b.timestamp AND r."projectId" = '${projectId}'` + ) + ) ) .groupBy("timestamp") .orderBy("timestamp"); diff --git a/apps/server/src/app/reporting/dto/create-report.dto.ts b/apps/server/src/app/reporting/dto/create-report.dto.ts index 9e2d3b7c..28185ad5 100644 --- a/apps/server/src/app/reporting/dto/create-report.dto.ts +++ b/apps/server/src/app/reporting/dto/create-report.dto.ts @@ -36,8 +36,8 @@ export class PromptExecutionMetadataDto { type: String, }) @IsString() - provider: string - + provider: string; + @ApiProperty({ description: "Client name identifier", type: String, @@ -178,4 +178,4 @@ export class ExecutionCalculatedDto { export class CreateReportV3Dto extends CreateReportDto { @ValidateNested({ each: true }) calculated: ExecutionCalculatedDto; -} \ No newline at end of file +} diff --git a/apps/server/src/app/reporting/object-types/request-report-result.model.ts b/apps/server/src/app/reporting/object-types/request-report-result.model.ts index df91fe22..8488f069 100644 --- a/apps/server/src/app/reporting/object-types/request-report-result.model.ts +++ b/apps/server/src/app/reporting/object-types/request-report-result.model.ts @@ -16,4 +16,4 @@ export class PaginatedReportsResult { export class ReportResult { @Field(() => GraphQLJSONObject, { nullable: false }) data: SerializedReport; -} \ No newline at end of file +} diff --git a/apps/server/src/app/reporting/reporting.service.ts b/apps/server/src/app/reporting/reporting.service.ts index a6ef729d..28e5980e 100644 --- a/apps/server/src/app/reporting/reporting.service.ts +++ b/apps/server/src/app/reporting/reporting.service.ts @@ -171,9 +171,9 @@ export class ReportingService { query = query.limit(size).offset(offset); if (sort) { - query.orderBy(sort.field, sort.order) + query.orderBy(sort.field, sort.order); } else { - query.orderBy("timestamp", "desc") + query.orderBy("timestamp", "desc"); } try { diff --git a/apps/server/src/app/reporting/requests.resolver.ts b/apps/server/src/app/reporting/requests.resolver.ts index 1df28686..b1cb60af 100644 --- a/apps/server/src/app/reporting/requests.resolver.ts +++ b/apps/server/src/app/reporting/requests.resolver.ts @@ -32,7 +32,7 @@ export class RequestReportsResolver { @CurrentUser() user: RequestUser ): Promise { let project; - + try { project = await this.projectsService.getProjectById(data.projectId); @@ -46,7 +46,10 @@ export class RequestReportsResolver { throw new InternalServerErrorException(); } - const result = await this.reportingService.getReportById(data.reportId, data.projectId); + const result = await this.reportingService.getReportById( + data.reportId, + data.projectId + ); return result; } diff --git a/clickhouse/migrations/20231231065935_create_reports.ts b/clickhouse/migrations/20231231065935_create_reports.ts index 9ddbf697..1440bf19 100644 --- a/clickhouse/migrations/20231231065935_create_reports.ts +++ b/clickhouse/migrations/20231231065935_create_reports.ts @@ -3,38 +3,36 @@ import type { Knex } from "knex"; export async function up(knex: Knex): Promise { /* Create reports table */ await knex.schema.createTable("reports", (table) => { - table.string("id") - table.dateTime("timestamp") - table.string("environment") - table.string("organizationId") - table.string("projectId") - table.specificType("promptTokens", "Float64") - table.specificType("completionTokens", "Float64") - table.specificType("totalTokens", "Float64") - table.specificType("promptCost", "Float64") - table.specificType("completionCost", "Float64") - table.specificType("totalCost", "Float64") - table.specificType("duration", "UInt32") - table.string("type") - table.string("client") - table.string("clientVersion") - table.string("model") - table.string("provider") - table.string("modelAuthor") - table.dateTime("requestTimestamp") - table.string("requestBody") - table.boolean("isError") - table.specificType("responseStatusCode", "UInt32") - table.dateTime("responseTimestamp") - table.string("responseBody") - table.boolean("cacheEnabled") - table.boolean("cacheHit") + table.string("id"); + table.dateTime("timestamp"); + table.string("environment"); + table.string("organizationId"); + table.string("projectId"); + table.specificType("promptTokens", "Float64"); + table.specificType("completionTokens", "Float64"); + table.specificType("totalTokens", "Float64"); + table.specificType("promptCost", "Float64"); + table.specificType("completionCost", "Float64"); + table.specificType("totalCost", "Float64"); + table.specificType("duration", "UInt32"); + table.string("type"); + table.string("client"); + table.string("clientVersion"); + table.string("model"); + table.string("provider"); + table.string("modelAuthor"); + table.dateTime("requestTimestamp"); + table.string("requestBody"); + table.boolean("isError"); + table.specificType("responseStatusCode", "UInt32"); + table.dateTime("responseTimestamp"); + table.string("responseBody"); + table.boolean("cacheEnabled"); + table.boolean("cacheHit"); }); } - export async function down(knex: Knex): Promise { /* Drop reports table */ await knex.schema.dropTable("reports"); } - diff --git a/clickhouse/migrations/20231231100122_create_report_properties.ts b/clickhouse/migrations/20231231100122_create_report_properties.ts index c07716cf..08b5dc50 100644 --- a/clickhouse/migrations/20231231100122_create_report_properties.ts +++ b/clickhouse/migrations/20231231100122_create_report_properties.ts @@ -1,18 +1,16 @@ -import type { Knex } from "knex"; - -export async function up(knex: Knex): Promise { - /* Create report properties table */ - await knex.schema.createTable("reportProperties", (table) => { - table.string("id").defaultTo(knex.raw("generateUUIDv4()")) - table.string("reportId") - table.string("key") - table.string("value") - }); -} - - -export async function down(knex: Knex): Promise { - /* Drop report properties table */ - await knex.schema.dropTable("reportProperties"); -} - +import type { Knex } from "knex"; + +export async function up(knex: Knex): Promise { + /* Create report properties table */ + await knex.schema.createTable("reportProperties", (table) => { + table.string("id").defaultTo(knex.raw("generateUUIDv4()")); + table.string("reportId"); + table.string("key"); + table.string("value"); + }); +} + +export async function down(knex: Knex): Promise { + /* Drop report properties table */ + await knex.schema.dropTable("reportProperties"); +} diff --git a/clickhouse/tsconfig.json b/clickhouse/tsconfig.json index 3d7f4c9c..894d423b 100644 --- a/clickhouse/tsconfig.json +++ b/clickhouse/tsconfig.json @@ -8,6 +8,6 @@ "files": [], "include": [], "compilerOptions": { - "esModuleInterop": true, + "esModuleInterop": true } } diff --git a/docker-compose.infra.yaml b/docker-compose.infra.yaml index d62f30e4..f4f9798c 100644 --- a/docker-compose.infra.yaml +++ b/docker-compose.infra.yaml @@ -10,7 +10,7 @@ services: command: -c "npx knex migrate:latest" environment: CLICKHOUSE_HOST: clickhouse - CLICKHOUSE_PORT: '8123' + CLICKHOUSE_PORT: "8123" CLICKHOUSE_USER: default CLICKHOUSE_PASSWORD: default depends_on: @@ -45,7 +45,7 @@ services: - "9000:9000" - "9004:9004" healthcheck: - test: ["CMD-SHELL", "clickhouse-client --query \"SELECT 1\""] + test: ["CMD-SHELL", 'clickhouse-client --query "SELECT 1"'] interval: 5s timeout: 5s retries: 3 @@ -107,4 +107,4 @@ services: - "KMS_REGION=us-east-1" volumes: - postgres_data: ~ \ No newline at end of file + postgres_data: ~ diff --git a/libs/common/src/index.ts b/libs/common/src/index.ts index a89f9aec..2de413ec 100644 --- a/libs/common/src/index.ts +++ b/libs/common/src/index.ts @@ -1,4 +1,4 @@ import * as versionJson from "./version.json"; export * as MetricsTypes from "./metrics/types"; export * from "./utils"; -export const version = versionJson.version; \ No newline at end of file +export const version = versionJson.version; diff --git a/libs/common/src/metrics/types.ts b/libs/common/src/metrics/types.ts index ee4147d6..6cd8f0c3 100644 --- a/libs/common/src/metrics/types.ts +++ b/libs/common/src/metrics/types.ts @@ -7,7 +7,7 @@ export type SuccessErrorRateResultDataType = { timestamp: string; success: number; error: number; - total: number; + total: number; }[]; export type ModelUsageResultDataType = { @@ -15,4 +15,4 @@ export type ModelUsageResultDataType = { model: string; modelAuthor: string; value: string; -}[]; \ No newline at end of file +}[]; diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts index 5a169eca..6cc0f5ea 100644 --- a/libs/types/src/index.ts +++ b/libs/types/src/index.ts @@ -2,4 +2,4 @@ export * from "./prompt-execution-type"; export * from "./request"; export * from "./ts-helpers"; export * from "./provider.types"; -export * from "./reports"; \ No newline at end of file +export * from "./reports"; diff --git a/libs/types/src/reports.ts b/libs/types/src/reports.ts index de10092e..f1f8be36 100644 --- a/libs/types/src/reports.ts +++ b/libs/types/src/reports.ts @@ -40,9 +40,7 @@ export interface SerializedReport cacheHit: boolean; } -export const serializeReport = ( - doc: ReportSchema -): SerializedReport => { +export const serializeReport = (doc: ReportSchema): SerializedReport => { return { ...doc, requestBody: JSON.parse(doc.requestBody), @@ -53,7 +51,6 @@ export const serializeReport = ( }; }; - export interface PaginatedReportsSchema { id: string; environment: string; @@ -70,7 +67,8 @@ export interface PaginatedReportsSchema { promptId: string; } -export interface SerializedPaginatedReport extends Omit { +export interface SerializedPaginatedReport + extends Omit { cacheEnabled: boolean; cacheHit: boolean; } diff --git a/tsconfigz.json b/tsconfigz.json deleted file mode 100644 index ffcbb947..00000000 --- a/tsconfigz.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./tsconfig.base.json" -} From 39a4c6c6f2ea0e7ef112c7be8cc9d25a20f3ba42 Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 02:52:58 -0800 Subject: [PATCH 3/8] add mysql2 to implicitDeps --- apps/server/webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/server/webpack.config.js b/apps/server/webpack.config.js index 0084d777..a75d800e 100644 --- a/apps/server/webpack.config.js +++ b/apps/server/webpack.config.js @@ -49,6 +49,7 @@ function generatePackageJson() { "prisma", "prisma-nestjs-graphql", "pino-pretty", + "mysql2" ]; const dependencies = implicitDeps.reduce((acc, dep) => { acc[dep] = packageJson.dependencies[dep]; From a4d82114c8b00d9c8029e49cb698b304ff0e49e0 Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 02:56:41 -0800 Subject: [PATCH 4/8] fix formatting --- apps/server/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/webpack.config.js b/apps/server/webpack.config.js index a75d800e..c68851d5 100644 --- a/apps/server/webpack.config.js +++ b/apps/server/webpack.config.js @@ -49,7 +49,7 @@ function generatePackageJson() { "prisma", "prisma-nestjs-graphql", "pino-pretty", - "mysql2" + "mysql2", ]; const dependencies = implicitDeps.reduce((acc, dep) => { acc[dep] = packageJson.dependencies[dep]; From 3c4eb457ade5fbd58eba3131a70ca321f55b559d Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 04:42:47 -0800 Subject: [PATCH 5/8] update package json and knexfile --- clickhouse/knexfile.ts | 5 +++-- clickhouse/package-lock.json | 8 ++++---- clickhouse/package.json | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/clickhouse/knexfile.ts b/clickhouse/knexfile.ts index 8f27bb9f..9daf442c 100644 --- a/clickhouse/knexfile.ts +++ b/clickhouse/knexfile.ts @@ -8,11 +8,12 @@ const { CLICKHOUSE_PASSWORD = "default", } = process.env; +const str = `https://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default` + const config: { [key: string]: Knex.Config } = { default: { client: clickhouse as any, - connection: () => - `clickhouse://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default` as any, + connection: () => str as any, migrations: { disableTransactions: true, disableMigrationsListValidation: true, diff --git a/clickhouse/package-lock.json b/clickhouse/package-lock.json index b968d2e8..91a777e8 100644 --- a/clickhouse/package-lock.json +++ b/clickhouse/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@pezzo/knex-clickhouse-dialect": "^1.2.0" + "@pezzo/knex-clickhouse-dialect": "^1.3.0" }, "devDependencies": { "ts-node": "^10.9.2" @@ -53,9 +53,9 @@ } }, "node_modules/@pezzo/knex-clickhouse-dialect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", - "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.3.0.tgz", + "integrity": "sha512-iZlRl62do4lfSjHUfCTclZm/a3ZEnWFrm2P34yiOXMgqDX1m2VI/DAyLNuBmuvo+WIluFaW1O7SXMhlMMcnuHQ==", "dependencies": { "clickhouse": "^2.6.0", "knex": "^2.5.1", diff --git a/clickhouse/package.json b/clickhouse/package.json index 75f1fc10..a2c92c64 100644 --- a/clickhouse/package.json +++ b/clickhouse/package.json @@ -12,6 +12,6 @@ "ts-node": "^10.9.2" }, "dependencies": { - "@pezzo/knex-clickhouse-dialect": "^1.2.0" + "@pezzo/knex-clickhouse-dialect": "^1.3.0" } } diff --git a/package-lock.json b/package-lock.json index 87eb70a4..858c2119 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@nestjs/swagger": "^7.1.12", "@nx/nest": "16.6.0", "@nx/next": "16.6.0", - "@pezzo/knex-clickhouse-dialect": "^1.2.0", + "@pezzo/knex-clickhouse-dialect": "^1.3.0", "@pezzo/llm-toolkit": "^0.5.0", "@prisma/client": "^5.1.1", "@radix-ui/react-accordion": "^1.1.2", @@ -11291,9 +11291,9 @@ } }, "node_modules/@pezzo/knex-clickhouse-dialect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", - "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.3.0.tgz", + "integrity": "sha512-iZlRl62do4lfSjHUfCTclZm/a3ZEnWFrm2P34yiOXMgqDX1m2VI/DAyLNuBmuvo+WIluFaW1O7SXMhlMMcnuHQ==", "dependencies": { "clickhouse": "^2.6.0", "knex": "^2.5.1", @@ -58031,9 +58031,9 @@ } }, "@pezzo/knex-clickhouse-dialect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.2.0.tgz", - "integrity": "sha512-UrQGCidFMevZ+ZAzRD/aKthMK9SP/KQOMGPNVpVXJXWCZthj7GkTjRrpjqIpZ189e3nft/B7mu+aSStymw0BQA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@pezzo/knex-clickhouse-dialect/-/knex-clickhouse-dialect-1.3.0.tgz", + "integrity": "sha512-iZlRl62do4lfSjHUfCTclZm/a3ZEnWFrm2P34yiOXMgqDX1m2VI/DAyLNuBmuvo+WIluFaW1O7SXMhlMMcnuHQ==", "requires": { "clickhouse": "^2.6.0", "knex": "^2.5.1", diff --git a/package.json b/package.json index dcc150ae..dc0c14b8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@nestjs/swagger": "^7.1.12", "@nx/nest": "16.6.0", "@nx/next": "16.6.0", - "@pezzo/knex-clickhouse-dialect": "^1.2.0", + "@pezzo/knex-clickhouse-dialect": "^1.3.0", "@pezzo/llm-toolkit": "^0.5.0", "@prisma/client": "^5.1.1", "@radix-ui/react-accordion": "^1.1.2", From 168790700ead05bb4406ee36eace0cd971616fff Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 04:48:27 -0800 Subject: [PATCH 6/8] fix formatting --- clickhouse/knexfile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clickhouse/knexfile.ts b/clickhouse/knexfile.ts index 9daf442c..d849ac24 100644 --- a/clickhouse/knexfile.ts +++ b/clickhouse/knexfile.ts @@ -8,7 +8,7 @@ const { CLICKHOUSE_PASSWORD = "default", } = process.env; -const str = `https://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default` +const str = `https://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default`; const config: { [key: string]: Knex.Config } = { default: { From 4e694f22c82dc034bba56950a2fad640f7ecdffd Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 10:59:35 -0800 Subject: [PATCH 7/8] test failing migration script --- clickhouse/knexfile.ts | 4 +++- clickhouse/migrate.sh | 17 +++++++++++++++++ .../20231231100122_create_report_properties.ts | 2 +- docker-compose.infra.yaml | 2 +- docker-compose.yaml | 4 +++- 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100755 clickhouse/migrate.sh diff --git a/clickhouse/knexfile.ts b/clickhouse/knexfile.ts index d849ac24..f32e4b4a 100644 --- a/clickhouse/knexfile.ts +++ b/clickhouse/knexfile.ts @@ -2,13 +2,15 @@ import { Knex } from "knex"; import clickhouse from "@pezzo/knex-clickhouse-dialect"; const { + CLICKHOUSE_PROTOCOL = "http", CLICKHOUSE_HOST = "localhost", CLICKHOUSE_PORT = "8123", CLICKHOUSE_USER = "default", CLICKHOUSE_PASSWORD = "default", + CLICKHOUSE_DATABASE = "default", } = process.env; -const str = `https://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/default`; +const str = `${CLICKHOUSE_PROTOCOL}://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}`; const config: { [key: string]: Knex.Config } = { default: { diff --git a/clickhouse/migrate.sh b/clickhouse/migrate.sh new file mode 100755 index 00000000..47d9f07a --- /dev/null +++ b/clickhouse/migrate.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +echo "Running knex migrations" + +# Run the migration command +npx knex migrate:latest + +# Capture the exit status +exit_status=$? + +if [ $exit_status -ne 0 ]; then + echo "Error running knex migrations" +else + echo "Knex migrations ran successfully" +fi + +exit $exit_status \ No newline at end of file diff --git a/clickhouse/migrations/20231231100122_create_report_properties.ts b/clickhouse/migrations/20231231100122_create_report_properties.ts index 08b5dc50..dadbf916 100644 --- a/clickhouse/migrations/20231231100122_create_report_properties.ts +++ b/clickhouse/migrations/20231231100122_create_report_properties.ts @@ -4,7 +4,7 @@ export async function up(knex: Knex): Promise { /* Create report properties table */ await knex.schema.createTable("reportProperties", (table) => { table.string("id").defaultTo(knex.raw("generateUUIDv4()")); - table.string("reportId"); + table.string("reportId").references("id").inTable("reports"); table.string("key"); table.string("value"); }); diff --git a/docker-compose.infra.yaml b/docker-compose.infra.yaml index f4f9798c..2ece0455 100644 --- a/docker-compose.infra.yaml +++ b/docker-compose.infra.yaml @@ -7,7 +7,7 @@ services: dockerfile: ./apps/server/Dockerfile entrypoint: /bin/sh working_dir: /app/clickhouse - command: -c "npx knex migrate:latest" + command: -c "./migrate.sh" environment: CLICKHOUSE_HOST: clickhouse CLICKHOUSE_PORT: "8123" diff --git a/docker-compose.yaml b/docker-compose.yaml index 53f0b2b1..418b0e62 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -22,7 +22,9 @@ services: ports: - "3000:3000" depends_on: - pezzo-server-migrate: + pezzo-prisma-migrate: + condition: service_completed_successfully + pezzo-clickhouse-migrate: condition: service_completed_successfully postgres: condition: service_healthy From d9209df5cd69d6e5a9039e93480541e68be2b310 Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Sun, 31 Dec 2023 11:16:42 -0800 Subject: [PATCH 8/8] fix knex migration --- .../migrations/20231231100122_create_report_properties.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clickhouse/migrations/20231231100122_create_report_properties.ts b/clickhouse/migrations/20231231100122_create_report_properties.ts index dadbf916..08b5dc50 100644 --- a/clickhouse/migrations/20231231100122_create_report_properties.ts +++ b/clickhouse/migrations/20231231100122_create_report_properties.ts @@ -4,7 +4,7 @@ export async function up(knex: Knex): Promise { /* Create report properties table */ await knex.schema.createTable("reportProperties", (table) => { table.string("id").defaultTo(knex.raw("generateUUIDv4()")); - table.string("reportId").references("id").inTable("reports"); + table.string("reportId"); table.string("key"); table.string("value"); });