diff --git a/ee/tabby-ui/app/(dashboard)/cluster/components/cluster.tsx b/ee/tabby-ui/app/(dashboard)/cluster/components/cluster.tsx deleted file mode 100644 index d43994a4f809..000000000000 --- a/ee/tabby-ui/app/(dashboard)/cluster/components/cluster.tsx +++ /dev/null @@ -1,117 +0,0 @@ -'use client' - -import { noop } from 'lodash-es' -import { useQuery } from 'urql' - -import { graphql } from '@/lib/gql/generates' -import { WorkerKind } from '@/lib/gql/generates/graphql' -import { useHealth } from '@/lib/hooks/use-health' -import { useWorkers } from '@/lib/hooks/use-workers' -import { useMutation } from '@/lib/tabby/gql' -import { Button } from '@/components/ui/button' -import { IconRotate } from '@/components/ui/icons' -import { Input } from '@/components/ui/input' -import { Separator } from '@/components/ui/separator' -import { CopyButton } from '@/components/copy-button' - -import WorkerCard from './worker-card' - -const getRegistrationTokenDocument = graphql(/* GraphQL */ ` - query GetRegistrationToken { - registrationToken - } -`) - -const resetRegistrationTokenDocument = graphql(/* GraphQL */ ` - mutation ResetRegistrationToken { - resetRegistrationToken - } -`) - -function toBadgeString(str: string) { - return encodeURIComponent(str.replaceAll('-', '--')) -} - -export default function Workers() { - const { data: healthInfo } = useHealth() - const workers = useWorkers() - const [{ data: registrationTokenRes }, reexecuteQuery] = useQuery({ - query: getRegistrationTokenDocument - }) - - const resetRegistrationToken = useMutation(resetRegistrationTokenDocument, { - onCompleted() { - reexecuteQuery() - } - }) - - if (!healthInfo) return - - return ( -
-

- Congratulations, your tabby instance - is up! -

- - - - - - - {!!registrationTokenRes?.registrationToken && ( -
- Registration token: - - - -
- )} - -
- {!!workers?.[WorkerKind.Completion] && ( - <> - {workers[WorkerKind.Completion].map((worker, i) => { - return - })} - - )} - {!!workers?.[WorkerKind.Chat] && ( - <> - {workers[WorkerKind.Chat].map((worker, i) => { - return - })} - - )} - -
-
- ) -} diff --git a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx index cd9cd3d66d30..2d5ba85f256c 100644 --- a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx +++ b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx @@ -65,7 +65,7 @@ export default function Sidebar({ children, className }: SidebarProps) { } > - Cluster + System Jobs Reports Activities diff --git a/ee/tabby-ui/app/(dashboard)/system/components/cluster.tsx b/ee/tabby-ui/app/(dashboard)/system/components/cluster.tsx new file mode 100644 index 000000000000..9c77ea8d3121 --- /dev/null +++ b/ee/tabby-ui/app/(dashboard)/system/components/cluster.tsx @@ -0,0 +1,303 @@ +'use client' + +import bytes from 'bytes' +import { noop, sum } from 'lodash-es' +import { useTheme } from 'next-themes' +import { Cell, Label, Pie, PieChart, ResponsiveContainer } from 'recharts' +import { useQuery } from 'urql' + +import { graphql } from '@/lib/gql/generates' +import { + DiskUsage, + DiskUsageStats, + WorkerKind +} from '@/lib/gql/generates/graphql' +import { useHealth } from '@/lib/hooks/use-health' +import { useWorkers } from '@/lib/hooks/use-workers' +import { useMutation } from '@/lib/tabby/gql' +import { Button } from '@/components/ui/button' +import { IconRotate } from '@/components/ui/icons' +import { Input } from '@/components/ui/input' +import { Separator } from '@/components/ui/separator' +import { CopyButton } from '@/components/copy-button' +import LoadingWrapper from '@/components/loading-wrapper' + +import WorkerCard from './worker-card' + +const getRegistrationTokenDocument = graphql(/* GraphQL */ ` + query GetRegistrationToken { + registrationToken + } +`) + +const resetRegistrationTokenDocument = graphql(/* GraphQL */ ` + mutation ResetRegistrationToken { + resetRegistrationToken + } +`) + +function toBadgeString(str: string) { + return encodeURIComponent(str.replaceAll('-', '--')) +} + +export default function Workers() { + const { data: healthInfo } = useHealth() + const workers = useWorkers() + const [{ data: registrationTokenRes }, reexecuteQuery] = useQuery({ + query: getRegistrationTokenDocument + }) + + const resetRegistrationToken = useMutation(resetRegistrationTokenDocument, { + onCompleted() { + reexecuteQuery() + } + }) + + if (!healthInfo) return + + return ( +
+

+ Congratulations, your tabby instance + is up! +

+ + + + + + + {!!registrationTokenRes?.registrationToken && ( +
+ Registration token: + + + +
+ )} + +
+ {!!workers?.[WorkerKind.Completion] && ( + <> + {workers[WorkerKind.Completion].map((worker, i) => { + return + })} + + )} + {!!workers?.[WorkerKind.Chat] && ( + <> + {workers[WorkerKind.Chat].map((worker, i) => { + return + })} + + )} + +
+ + + +
+ ) +} + +export const getDiskUsageStats = graphql(/* GraphQL */ ` + query GetDiskUsageStats { + diskUsageStats { + events { + filePaths + size + } + indexedRepositories { + filePaths + size + } + database { + filePaths + size + } + models { + filePaths + size + } + } + } +`) + +type UsageItem = { + label: string + key: keyof DiskUsageStats + color: string +} + +type UsageItemWithSize = UsageItem & { size: number } + +const usageList: UsageItem[] = [ + { + label: 'Model', + key: 'models', + color: '#0088FE' + }, + { + label: 'Indexing', + key: 'indexedRepositories', + color: '#00C49F' + }, + { + label: 'Event Logs', + key: 'events', + color: '#FF8042' + }, + { + label: 'Other', + key: 'database', + color: '#FFBB28' + } +] + +function Usage() { + const [{ data, fetching }] = useQuery({ + query: getDiskUsageStats + }) + + let usageData: UsageItemWithSize[] = [] + let totalUsage: number = 0 + if (data) { + usageData = usageList + .map(usage => { + if (!data.diskUsageStats[usage.key]) return null + const diskUsage = data.diskUsageStats[usage.key] as DiskUsage + return { + ...usage, + size: diskUsage.size + } + }) + .filter(usage => usage) as UsageItemWithSize[] + totalUsage = sum(usageData.map(data => data.size)) + } + + return ( + }> + <> +
+

Disk Usage

+

+ Storage utilization by Type +

+
+
+ + + + {usageData.map(entry => ( + + ))} + + + + +
+ {usageData.map(usage => ( +
+
+
+

{usage!.label}

+
+

{toBytes(usage!.size)}

+
+ ))} +
+
+ + + ) +} + +function CustomLabel({ + viewBox, + totalUsage +}: { + viewBox?: { + cx: number + cy: number + } + totalUsage: number +}) { + const { theme } = useTheme() + if (!viewBox) return + const { cx, cy } = viewBox + return ( + + + Total Usage + + + {toBytes(totalUsage)} + + + ) +} + +function toBytes(value: number) { + return bytes(value * 1024, { unitSeparator: ' ' }) +} diff --git a/ee/tabby-ui/app/(dashboard)/cluster/components/worker-card.tsx b/ee/tabby-ui/app/(dashboard)/system/components/worker-card.tsx similarity index 100% rename from ee/tabby-ui/app/(dashboard)/cluster/components/worker-card.tsx rename to ee/tabby-ui/app/(dashboard)/system/components/worker-card.tsx diff --git a/ee/tabby-ui/app/(dashboard)/cluster/page.tsx b/ee/tabby-ui/app/(dashboard)/system/page.tsx similarity index 90% rename from ee/tabby-ui/app/(dashboard)/cluster/page.tsx rename to ee/tabby-ui/app/(dashboard)/system/page.tsx index 29c36a94a5ab..951e5e8c37ca 100644 --- a/ee/tabby-ui/app/(dashboard)/cluster/page.tsx +++ b/ee/tabby-ui/app/(dashboard)/system/page.tsx @@ -3,7 +3,7 @@ import { Metadata } from 'next' import ClusterInfo from './components/cluster' export const metadata: Metadata = { - title: 'Cluster' + title: 'System' } export default function IndexPage() { diff --git a/ee/tabby-ui/package.json b/ee/tabby-ui/package.json index c26ac86d2f69..c89ae345e304 100644 --- a/ee/tabby-ui/package.json +++ b/ee/tabby-ui/package.json @@ -53,6 +53,7 @@ "@vercel/kv": "^0.2.1", "@vercel/og": "^0.5.7", "ai": "^2.1.6", + "bytes": "^3.1.2", "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", "cmdk": "^1.0.0", @@ -107,6 +108,7 @@ "@ianvs/prettier-plugin-sort-imports": "^4.1.1", "@parcel/watcher": "^2.3.0", "@tailwindcss/typography": "^0.5.9", + "@types/bytes": "^3.1.4", "@types/humanize-duration": "^3.27.4", "@types/lodash-es": "^4.17.10", "@types/node": "^17.0.12", diff --git a/ee/tabby-ui/yarn.lock b/ee/tabby-ui/yarn.lock index 93421fa8bb54..a98fab2a499e 100644 --- a/ee/tabby-ui/yarn.lock +++ b/ee/tabby-ui/yarn.lock @@ -2497,6 +2497,11 @@ lodash.merge "^4.6.2" postcss-selector-parser "6.0.10" +"@types/bytes@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.4.tgz#8563f38ea6096df3f409c6500e8ac171790a7c1f" + integrity sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA== + "@types/chroma-js@^2.4.3": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-2.4.4.tgz#254dddff54568ff8e5d0dcdb071871a458fdfd31" @@ -3403,6 +3408,11 @@ busboy@1.6.0, busboy@^1.6.0: dependencies: streamsearch "^1.1.0" +bytes@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"