import { Badge } from "@/components/ui/badge"; import { CardHeader, CardTitle } from "@/components/ui/card"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { resetDurationLabels } from "@/lib/constants/governance"; import { useGetProviderGovernanceQuery } from "@/lib/store"; import { ModelProvider } from "@/lib/types/config"; import { cn } from "@/lib/utils"; import { formatCurrency } from "@/lib/utils/governance"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; interface Props { className?: string; provider: ModelProvider; } // Helper to format reset duration for display const formatResetDuration = (duration: string) => { return resetDurationLabels[duration] || duration; }; // Circular progress component function CircularProgress({ value, max, size = 80, strokeWidth = 6, isExhausted = false, }: { value: number; max: number; size?: number; strokeWidth?: number; isExhausted?: boolean; }) { const percentage = max > 0 ? Math.min((value / max) * 100, 100) : 0; const radius = (size - strokeWidth) / 2; const circumference = radius * 2 * Math.PI; const strokeDashoffset = circumference - (percentage / 100) * circumference; return (
{/* Background circle */} {/* Progress circle */} 80 ? "text-amber-500/70" : "text-emerald-500/70", )} />
80 ? "text-amber-500/70" : "text-foreground")} > {Math.round(percentage)}%
); } // Metric card component function MetricCard({ title, value, max, unit, resetDuration, isExhausted, }: { title: string; value: number; max: number; unit: string; resetDuration: string; isExhausted: boolean; }) { // Compute safe percentage to avoid division by zero const percentage = max > 0 ? Math.round((value / max) * 100) : 0; const clampedPercentage = Math.max(0, Math.min(100, percentage)); return (
{/* Subtle gradient overlay */}
{title} {isExhausted && ( Exhausted )}
{unit === "$" ? formatCurrency(value) : value.toLocaleString()} / {unit === "$" ? formatCurrency(max) : `${max.toLocaleString()} ${unit}`}
Resets {formatResetDuration(resetDuration)}

{clampedPercentage}% of {title.toLowerCase()} used

); } export default function ProviderGovernanceTable({ provider, className }: Props) { const hasViewAccess = useRbac(RbacResource.Governance, RbacOperation.View); const { data: providerGovernanceData, isLoading } = useGetProviderGovernanceQuery(undefined, { skip: !hasViewAccess, pollingInterval: 5000, }); // Find governance data for this provider const providerGovernance = providerGovernanceData?.providers?.find((p) => p.provider === provider.name); // Check if any governance is configured const hasGovernance = providerGovernance?.budget || providerGovernance?.rate_limit; if (isLoading) { return (
Governance
); } // Governance not enabled or no governance configured - don't show the section if (!hasGovernance) { return null; } const budget = providerGovernance?.budget; const rateLimit = providerGovernance?.rate_limit; const isBudgetExhausted = !!(budget?.max_limit && budget.max_limit > 0 && budget.current_usage >= budget.max_limit); const isTokenExhausted = !!( rateLimit?.token_max_limit && rateLimit.token_max_limit > 0 && rateLimit.token_current_usage >= rateLimit.token_max_limit ); const isRequestExhausted = !!( rateLimit?.request_max_limit && rateLimit.request_max_limit > 0 && rateLimit.request_current_usage >= rateLimit.request_max_limit ); return (
Governance
{/* Budget Card */} {budget && ( )} {/* Token Rate Limit Card */} {rateLimit?.token_max_limit && ( )} {/* Request Rate Limit Card */} {rateLimit?.request_max_limit && ( )}
); }