import FullPageLoader from "@/components/fullPageLoader"; import { NoPermissionView } from "@/components/noPermissionView"; import { ProviderNames } from "@/lib/constants/logs"; import { useGetModelsQuery, useGetProvidersQuery, useLazyGetLogsModelHistogramQuery, useLazyGetLogsStatsQuery } from "@/lib/store"; import { KnownProvider } from "@/lib/types/config"; import { LogStats } from "@/lib/types/logs"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { useEffect, useMemo, useState } from "react"; import { ModelCatalogEmptyState } from "./modelCatalogEmptyState"; import ModelCatalogTable, { ModelCatalogRow } from "./modelCatalogTable"; export default function ModelCatalogView() { const hasAccess = useRbac(RbacResource.ModelProvider, RbacOperation.View); const [providerFilter, setProviderFilter] = useState(""); const [statsMap, setStatsMap] = useState>(new Map()); const [modelsUsedMap, setModelsUsedMap] = useState>(new Map()); const [isLoadingModels, setIsLoadingModels] = useState(true); const { data: providers, isLoading: isLoadingProviders, error: providersError, refetch: refetchProviders, } = useGetProvidersQuery(undefined, { skip: !hasAccess }); const { data: modelsData } = useGetModelsQuery({ unfiltered: true }, { skip: !hasAccess }); // Global 24h stats for summary cards (lazy so we get fresh timestamps) const [triggerGlobalStats, { data: globalStats }] = useLazyGetLogsStatsQuery(); // Per-provider traffic stats (lazy, fired when providers load) const [triggerStats] = useLazyGetLogsStatsQuery(); const [triggerModelHistogram] = useLazyGetLogsModelHistogramQuery(); useEffect(() => { if (!hasAccess) return; const now = new Date().toISOString(); const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); triggerGlobalStats({ filters: { start_time: dayAgo, end_time: now } }); }, [hasAccess, triggerGlobalStats]); useEffect(() => { if (!providers || providers.length === 0) return; let cancelled = false; const now = new Date().toISOString(); const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); Promise.all( providers.map((p) => triggerStats({ filters: { providers: [p.name], start_time: dayAgo, end_time: now } }) .unwrap() .then((stats) => [p.name, stats] as const) .catch( () => [ p.name, { total_requests: 0, success_rate: 0, user_facing_success_rate: 0, average_latency: 0, user_facing_total_requests:0, total_tokens: 0, total_cost: 0 }, ] as const, ), ), ).then((results) => { if (!cancelled) setStatsMap(new Map(results)); }); return () => { cancelled = true; }; }, [providers, triggerStats]); // Per-provider models used in last 30 days useEffect(() => { if (!providers || providers.length === 0) return; let cancelled = false; setIsLoadingModels(true); const now = new Date().toISOString(); const monthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); Promise.all( providers.map((p) => triggerModelHistogram({ filters: { providers: [p.name], start_time: monthAgo, end_time: now } }) .unwrap() .then((data): [string, string[]] => [p.name, data.models ?? []]) .catch((): [string, string[]] => [p.name, []]), ), ).then((results) => { if (!cancelled) { setModelsUsedMap(new Map(results)); setIsLoadingModels(false); } }); return () => { cancelled = true; }; }, [providers, triggerModelHistogram]); // Build table rows const rows: ModelCatalogRow[] = useMemo(() => { if (!providers) return []; return providers.map((p) => { const isCustom = !ProviderNames.includes(p.name as KnownProvider); const modelsUsed = modelsUsedMap.get(p.name) ?? []; const providerStats = statsMap.get(p.name); const totalTraffic24h = providerStats?.total_requests ?? 0; const totalCost24h = providerStats?.total_cost ?? 0; return { providerName: p.name, isCustom, baseProviderType: p.custom_provider_config?.base_provider_type, modelsUsed, totalTraffic24h, totalCost24h, }; }); }, [providers, statsMap, modelsUsedMap]); // Filter rows by provider const filteredRows = useMemo(() => { if (!providerFilter) return rows; return rows.filter((r) => r.providerName === providerFilter); }, [rows, providerFilter]); if (isLoadingProviders) { return ; } if (!hasAccess) { return ; } if (providersError) { return (

Failed to load providers

); } if (!providers || providers.length === 0) { return ; } return (
p.name)} providerFilter={providerFilter} onProviderFilterChange={setProviderFilter} totalProviders={(providers ?? []).length} totalModels={modelsData?.total ?? 0} totalRequests24h={globalStats?.total_requests ?? 0} totalCost24h={globalStats?.total_cost ?? 0} isLoadingModels={isLoadingModels} />
); }