/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useQuery } from '@tanstack/react-query' import { AlertTriangle, HeartPulse, Timer } from 'lucide-react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { StaticDataTable, staticDataTableClassNames as tableStyles, } from '@/components/data-table' import { GroupBadge } from '@/components/group-badge' import { getPerfMetrics } from '@/features/performance-metrics/api' import { formatLatency, formatThroughput, formatUptimePct, getSuccessRateTextClass, } from '@/features/performance-metrics/lib/format' import type { PerformanceGroup } from '@/features/performance-metrics/types' import { cn } from '@/lib/utils' import { type UptimeDayPoint } from '../lib/mock-stats' import type { PricingModel } from '../types' import { LatencyTrendChart, UptimeTrendChart } from './model-details-charts' import { UptimeSparkline } from './model-details-uptime-sparkline' function StatCard(props: { icon: React.ComponentType<{ className?: string }> label: string value: React.ReactNode hint?: string valueClassName?: string }) { const Icon = props.icon return (
{props.label} {props.value} {props.hint && ( {props.hint} )}
) } type PerformanceRow = { group: string avg_ttft_ms: number avg_latency_ms: number success_rate: number avg_tps: number } function toUptimePct(value: number): number { if (!Number.isFinite(value)) return 0 const clamped = Math.min(100, Math.max(0, value)) return Math.round(clamped * 100) / 100 } function toLatencySeries(groups: PerformanceGroup[]) { const byTs = new Map() for (const group of groups) { for (const point of group.series) { if (point.avg_ttft_ms <= 0) continue const current = byTs.get(point.ts) ?? [] current.push(point.avg_ttft_ms) byTs.set(point.ts, current) } } return Array.from(byTs.entries()) .sort(([a], [b]) => a - b) .map(([ts, values]) => ({ timestamp: new Date(ts * 1000).toISOString(), group: 'latency', ttft_ms: Math.round( values.reduce((sum, value) => sum + value, 0) / values.length ), })) } function toUptimeSeries(groups: PerformanceGroup[]): UptimeDayPoint[] { const byTs = new Map() for (const group of groups) { for (const point of group.series) { const current = byTs.get(point.ts) ?? { rates: [], incidents: 0 } if (Number.isFinite(point.success_rate)) { const successRate = toUptimePct(point.success_rate) current.rates.push(successRate) if (successRate < 100) current.incidents += 1 } byTs.set(point.ts, current) } } return Array.from(byTs.entries()) .sort(([a], [b]) => a - b) .map(([ts, value]) => { const uptime = value.rates.length > 0 ? value.rates.reduce((sum, rate) => sum + rate, 0) / value.rates.length : 0 return { date: new Date(ts * 1000).toISOString(), uptime_pct: toUptimePct(uptime), incidents: value.incidents, outage_minutes: 0, } }) } function toGroupUptimeSeries(group: PerformanceGroup): UptimeDayPoint[] { return group.series.map((point) => { const successRate = toUptimePct(point.success_rate) return { date: new Date(point.ts * 1000).toISOString(), uptime_pct: successRate, incidents: successRate < 100 ? 1 : 0, outage_minutes: 0, } }) } function average( rows: PerformanceRow[], field: 'avg_ttft_ms' | 'avg_latency_ms' ) { const values = rows.map((row) => row[field]).filter((value) => value > 0) if (values.length === 0) return 0 return Math.round( values.reduce((sum, value) => sum + value, 0) / values.length ) } export function ModelDetailsPerformance(props: { model: PricingModel }) { const { t } = useTranslation() const metricsQuery = useQuery({ queryKey: ['perf-metrics', props.model.model_name], queryFn: () => getPerfMetrics(props.model.model_name, 24), staleTime: 60 * 1000, }) const groups = useMemo( () => metricsQuery.data?.data.groups ?? [], [metricsQuery.data] ) const performances = useMemo( () => groups.map((group) => ({ group: group.group, avg_ttft_ms: group.avg_ttft_ms, avg_latency_ms: group.avg_latency_ms, success_rate: group.success_rate, avg_tps: group.avg_tps, })), [groups] ) const latencySeries = useMemo(() => toLatencySeries(groups), [groups]) const uptimeSeries = useMemo(() => toUptimeSeries(groups), [groups]) const uptimeByGroup = useMemo>(() => { const map: Record = {} for (const group of groups) { map[group.group] = toGroupUptimeSeries(group) } return map }, [groups]) if (metricsQuery.isLoading || performances.length === 0) { return (
{t('Performance data is not yet available for this model.')}
) } const tpsValues = performances .map((p) => p.avg_tps) .filter((value) => value > 0) const avgTps = tpsValues.length > 0 ? tpsValues.reduce((sum, value) => sum + value, 0) / tpsValues.length : 0 const avgLatency = average(performances, 'avg_latency_ms') const successRates = performances .map((perf) => perf.success_rate) .filter((value) => Number.isFinite(value)) const successRate = successRates.length > 0 ? successRates.reduce((sum, value) => sum + value, 0) / successRates.length : 0 const incidentCount = uptimeSeries.reduce((s, p) => s + p.incidents, 0) return (
0 ? t('{{count}} incidents in the last 24 hours', { count: incidentCount, }) : t('No incidents in the last 24 hours') } valueClassName={getSuccessRateTextClass(successRate)} />
perf.group} columns={[ { id: 'group', header: t('Group'), className: tableStyles.compactHeaderCell, cellClassName: tableStyles.compactCell, cell: (perf) => , }, { id: 'tps', header: 'TPS', className: tableStyles.compactHeaderCellRight, cellClassName: tableStyles.compactNumericCell, cell: (perf) => formatThroughput(perf.avg_tps), }, { id: 'ttft', header: t('Average TTFT'), className: tableStyles.compactHeaderCellRight, cellClassName: tableStyles.compactNumericCell, cell: (perf) => formatLatency(perf.avg_ttft_ms), }, { id: 'latency', header: t('Average latency'), className: tableStyles.compactHeaderCellRight, cellClassName: tableStyles.compactMutedNumericCell, cell: (perf) => formatLatency(perf.avg_latency_ms), }, { id: 'success', header: t('Success rate'), className: cn(tableStyles.compactHeaderCell, 'min-w-[180px]'), cellClassName: tableStyles.compactCell, cell: (perf) => ( ), }, ]} />
0 ? t( 'Request success rate; {{incidents}} incident buckets in the last 24 hours', { incidents: incidentCount, } ) : t('Request success rate sampled over the last 24 hours') } accent={ incidentCount > 0 ? ( {t('{{count}} incidents', { count: incidentCount, })} ) : null } />
) } function SectionHeader(props: { icon: React.ComponentType<{ className?: string }> title: string description?: string accent?: React.ReactNode }) { const Icon = props.icon return (
{props.title}
{props.description && (

{props.description}

)}
{props.accent && (
{props.accent}
)}
) }