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/.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/.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 00000000..60dea32b
Binary files /dev/null and b/apps/console/src/assets/providers/anthropic-logo.png differ
diff --git a/apps/console/src/assets/providers/anthropic-logo.svg b/apps/console/src/assets/providers/anthropic-logo.svg
deleted file mode 100644
index 1138a04e..00000000
--- a/apps/console/src/assets/providers/anthropic-logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ 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 00000000..66ebdbbd
Binary files /dev/null and b/apps/console/src/assets/providers/azure-logo.png differ
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 00000000..90bfc0e1
Binary files /dev/null and b/apps/console/src/assets/providers/meta-logo.png differ
diff --git a/apps/console/src/assets/providers/mistral-logo.png b/apps/console/src/assets/providers/mistral-logo.png
new file mode 100644
index 00000000..ff9cbaaa
Binary files /dev/null and b/apps/console/src/assets/providers/mistral-logo.png differ
diff --git a/apps/console/src/assets/providers/openai-logo.png b/apps/console/src/assets/providers/openai-logo.png
new file mode 100644
index 00000000..c608558c
Binary files /dev/null and b/apps/console/src/assets/providers/openai-logo.png differ
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 @@
-
diff --git a/apps/console/src/components/metrics/StatisticBox.tsx b/apps/console/src/components/metrics/StatisticBox.tsx
index 1d2783f4..1a471993 100644
--- a/apps/console/src/components/metrics/StatisticBox.tsx
+++ b/apps/console/src/components/metrics/StatisticBox.tsx
@@ -33,6 +33,7 @@ const getProperties = (
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..43956433 100644
--- a/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx
+++ b/apps/console/src/components/prompts/editor/ProviderSelector/providers.tsx
@@ -3,19 +3,13 @@ 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[] = [
{
- image: (
-
- ),
+ image: ,
value: PromptService.OpenAiChatCompletion,
label: promptProvidersMapping[PromptService.OpenAiChatCompletion].name,
},
@@ -24,7 +18,7 @@ export const providersList: ProviderProps[] = [
),
value: PromptService.AzureOpenAiChatCompletion,
@@ -32,11 +26,7 @@ export const providersList: ProviderProps[] = [
},
{
image: (
-
+
),
value: PromptService.AnthropicCompletion,
label: promptProvidersMapping[PromptService.AnthropicCompletion].name,
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..43bbef7b 100644
--- a/apps/console/src/components/requests/RequestResponseViewJsonView.tsx
+++ b/apps/console/src/components/requests/RequestResponseViewJsonView.tsx
@@ -1,23 +1,22 @@
-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 +24,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..fbb580a2 100644
--- a/apps/console/src/graphql/definitions/queries/metrics.tsx
+++ b/apps/console/src/graphql/definitions/queries/metrics.tsx
@@ -1,28 +1,20 @@
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) {
+export const GET_PROJECT_METRIC_DELTA = graphql(/* GraphQL */ `
+ query getProjectMetricDelta($data: GetProjectMetricDeltaInput!) {
+ projectMetricDelta(data: $data) {
currentValue
previousValue
}
}
`);
-
-export const GET_PROJECT_METRIC_HISTOGRAM = graphql(/* GraphQL */ `
- query getProjectMetricHistogram($data: GetProjectMetricHistogramInput!) {
- projectMetricHistogram(data: $data) {
- date
- value
- }
- }
-`);
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..3bb2897e 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 = {}
) => {
- const { project } = useCurrentProject();
const result = useQuery({
- enabled: !!project,
- queryKey: ["projectMetric", ...Object.values(data)],
+ queryKey: ["report", data.reportId],
queryFn: () =>
- gqlClient.request(GET_PROJECT_METRIC, {
+ gqlClient.request(GET_REPORT, {
data,
}),
...options,
});
- return { ...result, data: result.data?.projectMetric };
+ return { ...result, report: result.data?.report as SerializedReport };
+};
+
+export const useGenericProjectMetricHistogram = (
+ data: GetGenericProjectMetricHistogramQueryVariables["data"],
+ options: UseQueryOptions<
+ GetGenericProjectMetricHistogramQuery,
+ GraphQLErrorResponse
+ > = {}
+) => {
+ const result = useQuery({
+ queryKey: ["genericProjectMetricHistogram", ...Object.values(data)],
+ queryFn: () =>
+ gqlClient.request(GET_GENERIC_PROJECT_METRIC_HISTOGRAM, {
+ data,
+ }),
+ ...options,
+ });
+
+ 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..240e8cdc 100644
--- a/apps/console/src/graphql/types.ts
+++ b/apps/console/src/graphql/types.ts
@@ -1,10 +1,4 @@
-import {
- ObservabilityReportMetadata,
- ObservabilityReportProperties,
- ObservabilityRequest,
- ObservabilityResponse,
- Provider,
-} from "@pezzo/types";
+import { Provider } from "@pezzo/types";
import { GraphQLError } from "graphql-request/build/esm/types";
export interface GraphQLErrorResponse {
@@ -15,15 +9,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;
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..8cb4c364 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..01f95465 100644
--- a/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx
+++ b/apps/console/src/pages/projects/overview/charts/ExecutionTimeChart.tsx
@@ -13,20 +13,11 @@ 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 {
- HistogramMetric,
- ProjectMetricType,
-} from "~/@generated/graphql/graphql";
+import { useGenericProjectMetricHistogram } from "~/graphql/hooks/queries";
+import { 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,19 +25,20 @@ export const ExecutionTimeChart = () => {
const controls = useProjectMetricControls();
const { filters } = useFiltersAndSortParams();
- const durationHistogram = useProjectMetricHistogram(
- {
- projectId: project?.id,
- metric: ProjectMetricType.Duration,
- 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 (
@@ -65,7 +57,10 @@ export const ExecutionTimeChart = () => {
);
}
- const data = histogramToChartData(durationHistogram.histogram);
+ const data = durationHistogram.histogram.data.map((d) => ({
+ timestamp: d.timestamp,
+ value: d.value,
+ }));
return (
@@ -85,19 +80,21 @@ 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`
}
/>
+ }
+ /> */}
+
+ {/* {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..686b5153 100644
--- a/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx
+++ b/apps/console/src/pages/projects/overview/charts/SuccessErrorRateChart.tsx
@@ -9,32 +9,15 @@ import {
BarChart,
} from "recharts";
import colors from "tailwindcss/colors";
-import { useProjectMetricHistogram } from "~/graphql/hooks/queries";
-import {
- HistogramMetric,
- ProjectMetricType,
-} from "~/@generated/graphql/graphql";
+import { HistogramIdType } 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";
-
-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,37 +25,22 @@ export const SuccessErrorRateChart = () => {
const { filters } = useFiltersAndSortParams();
const { project } = useCurrentProject();
- const totalRequestsHistogram = useProjectMetricHistogram(
- {
- projectId: project?.id,
- metric: ProjectMetricType.Requests,
- bucketSize: controls.bucketSize,
- startDate: startDate,
- endDate: endDate,
- filters,
- },
- {
- enabled: !!project && !!startDate && !!endDate,
- }
- );
-
- const erroneousRequestsHistogram = useProjectMetricHistogram(
- {
- projectId: project?.id,
- metric: ProjectMetricType.ErroneousRequests,
- bucketSize: controls.bucketSize,
- startDate: startDate,
- endDate: endDate,
- },
- {
- 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 (
- totalRequestsHistogram.isLoading ||
- erroneousRequestsHistogram.isLoading
- ) {
+ if (histogram.isLoading) {
return (
{
);
}
- const data = histogramToChartData(
- totalRequestsHistogram.histogram,
- erroneousRequestsHistogram.histogram
- );
+ const data = histogram.histogram.data;
return (
@@ -117,23 +82,28 @@ 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..f467886b 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";
@@ -40,12 +40,6 @@ export const PromptNavigation = () => {
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 (
+
+
+
{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 && (
+
+
+
)}
-
-
+
-
+
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..7759c0c3
--- /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;
+};
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/.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/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..693bfc67
--- /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..780b573f 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,33 @@ 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..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,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,33 @@ 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;
+}
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..9c890331 100644
--- a/apps/server/src/app/metrics/metrics.module.ts
+++ b/apps/server/src/app/metrics/metrics.module.ts
@@ -1,19 +1,13 @@
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],
- providers: [
- MetricsResolver,
- ProjectMetricsResolver,
- PrismaService,
- ProjectMetricsService,
- ],
+ imports: [IdentityModule, PromptsModule, ClickhHouseModule],
+ providers: [ProjectMetricsResolver, PrismaService, ProjectMetricsService],
})
export class MetricsModule {}
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..8fb86d5a 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;
+}
diff --git a/apps/server/src/app/metrics/project-metrics.resolver.ts b/apps/server/src/app/metrics/project-metrics.resolver.ts
index 5a8f388e..d85e1d54 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,16 @@ 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 +44,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 +71,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..253f4b36 100644
--- a/apps/server/src/app/metrics/project-metrics.service.ts
+++ b/apps/server/src/app/metrics/project-metrics.service.ts
@@ -1,256 +1,130 @@
-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 {
- buildBaseProjectMetricQuery,
- getStartAndEndDates,
-} from "./metrics.utils";
+ BadRequestException,
+ Injectable,
+ InternalServerErrorException,
+} from "@nestjs/common";
+import {
+ 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) {}
+ constructor(private clickHouseService: ClickHouseService) {}
- async getRequests(
+ 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);
-
- 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;
- };
-
- 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(
- 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
- ).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..87b61541
--- /dev/null
+++ b/apps/server/src/app/metrics/queries/average-request-duration-query.ts
@@ -0,0 +1,44 @@
+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;
+};
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..61dcc7d7
--- /dev/null
+++ b/apps/server/src/app/metrics/queries/model-usage-query.ts
@@ -0,0 +1,50 @@
+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..d881686a
--- /dev/null
+++ b/apps/server/src/app/metrics/queries/success-error-rate-query.ts
@@ -0,0 +1,50 @@
+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..28185ad5 100644
--- a/apps/server/src/app/reporting/dto/create-report.dto.ts
+++ b/apps/server/src/app/reporting/dto/create-report.dto.ts
@@ -18,12 +18,25 @@ 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",
@@ -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;
+}
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..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
@@ -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;
+}
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..28e5980e 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..b1cb60af 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,41 @@ 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/apps/server/webpack.config.js b/apps/server/webpack.config.js
index 0084d777..c68851d5 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];
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..f32e4b4a
--- /dev/null
+++ b/clickhouse/knexfile.ts
@@ -0,0 +1,26 @@
+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 = `${CLICKHOUSE_PROTOCOL}://${CLICKHOUSE_USER}:${CLICKHOUSE_PASSWORD}@${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}`;
+
+const config: { [key: string]: Knex.Config } = {
+ default: {
+ client: clickhouse as any,
+ connection: () => str as any,
+ migrations: {
+ disableTransactions: true,
+ disableMigrationsListValidation: true,
+ },
+ },
+};
+
+module.exports = config;
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/20231231065935_create_reports.ts b/clickhouse/migrations/20231231065935_create_reports.ts
new file mode 100644
index 00000000..1440bf19
--- /dev/null
+++ b/clickhouse/migrations/20231231065935_create_reports.ts
@@ -0,0 +1,38 @@
+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..08b5dc50
--- /dev/null
+++ b/clickhouse/migrations/20231231100122_create_report_properties.ts
@@ -0,0 +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");
+}
diff --git a/clickhouse/package-lock.json b/clickhouse/package-lock.json
new file mode 100644
index 00000000..91a777e8
--- /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.3.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.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",
+ "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..a2c92c64
--- /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.3.0"
+ }
+}
diff --git a/clickhouse/tsconfig.json b/clickhouse/tsconfig.json
new file mode 100644
index 00000000..894d423b
--- /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..2ece0455 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 "./migrate.sh"
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
@@ -116,4 +108,3 @@ services:
volumes:
postgres_data: ~
- opensearch-data1: ~
diff --git a/docker-compose.yaml b/docker-compose.yaml
index a9af9bc2..418b0e62 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,19 +16,21 @@ 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:
- "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
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..2de413ec 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;
diff --git a/libs/common/src/metrics/types.ts b/libs/common/src/metrics/types.ts
new file mode 100644
index 00000000..6cd8f0c3
--- /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;
+}[];
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..6cc0f5ea 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";
diff --git a/libs/types/src/reports.ts b/libs/types/src/reports.ts
new file mode 100644
index 00000000..f1f8be36
--- /dev/null
+++ b/libs/types/src/reports.ts
@@ -0,0 +1,84 @@
+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..858c2119 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.3.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.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",
+ "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.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",
+ "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..dc0c14b8 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.3.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",