import { useState } from 'react' import { type ColumnDef } from '@tanstack/react-table' import { Route, CircleAlert, Sparkles, KeyRound } from 'lucide-react' import { useTranslation } from 'react-i18next' import { formatBillingCurrencyFromUSD } from '@/lib/currency' import { formatUseTime, formatLogQuota, formatTimestampToDate, } from '@/lib/format' import { getAvatarColorClass } from '@/lib/colors' import { cn } from '@/lib/utils' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' import { DataTableColumnHeader } from '@/components/data-table' import { StatusBadge, type StatusBadgeProps, dotColorMap, textColorMap, } from '@/components/status-badge' import type { UsageLog } from '../../data/schema' import { getTimeColor, formatModelName, getTieredBillingSummary, hasAnyCacheTokens, parseLogOther, isViolationFeeLog, } from '../../lib/format' import { isDisplayableLogType, isTimingLogType, getLogTypeConfig, isPerCallBilling, } from '../../lib/utils' import type { LogOtherData } from '../../types' import { DetailsDialog } from '../dialogs/details-dialog' import { useUsageLogsContext } from '../usage-logs-provider' interface DetailSegment { text: string muted?: boolean danger?: boolean } function formatRatioCompact(ratio: number | undefined): string { if (ratio == null || !Number.isFinite(ratio)) return '-' return ratio % 1 === 0 ? String(ratio) : ratio.toFixed(4) } function buildDetailSegments( log: UsageLog, other: LogOtherData | null, t: (key: string, opts?: Record) => string ): DetailSegment[] { if (log.type === 6) { return [{ text: t('Async task refund') }] } if (log.type !== 2) return [] const isViolation = isViolationFeeLog(other) if (isViolation) { const segments: DetailSegment[] = [] segments.push({ text: t('Violation Fee'), danger: true }) if (other?.violation_fee_code) { segments.push({ text: other.violation_fee_code, muted: true, }) } segments.push({ text: `${t('Fee')}: ${formatLogQuota(other?.fee_quota ?? log.quota)}`, muted: true, }) return segments } if (!other) return [] const segments: DetailSegment[] = [] const priceOpts = { digitsLarge: 4, digitsSmall: 6, abbreviate: false } const formatPrice = (price: number) => `${formatBillingCurrencyFromUSD(price, priceOpts)}/M` const formatPriceCompact = (price: number) => formatBillingCurrencyFromUSD(price, priceOpts) const formatPriceList = (prices: string[], showUnit: boolean) => { const text = prices.join(' / ') return showUnit ? `${text}/M` : text } const tieredSummary = getTieredBillingSummary(other) if (tieredSummary) { const baseEntries = tieredSummary.priceEntries .filter((entry) => ['inputPrice', 'outputPrice'].includes(entry.field)) .map((entry) => formatPriceCompact(entry.price)) if (baseEntries.length > 0) { const tierLabel = tieredSummary.tier.label || t('Default') segments.push({ text: `${tierLabel} · ${formatPriceList(baseEntries, true)}`, }) } const cacheEntries = tieredSummary.priceEntries .filter((entry) => [ 'cacheReadPrice', 'cacheCreatePrice', 'cacheCreate1hPrice', ].includes(entry.field) ) .map((entry) => { return formatPriceCompact(entry.price) }) if (cacheEntries.length > 0) { segments.push({ text: `${t('Cache')} ${formatPriceList(cacheEntries, false)}`, muted: true, }) } const otherEntries = tieredSummary.priceEntries .filter( (entry) => ![ 'inputPrice', 'outputPrice', 'cacheReadPrice', 'cacheCreatePrice', 'cacheCreate1hPrice', ].includes(entry.field) ) .map((entry) => `${t(entry.shortLabel)} ${formatPrice(entry.price)}`) if (otherEntries.length > 0) { segments.push({ text: otherEntries.join(' · '), muted: true, }) } } else { const isPerCall = isPerCallBilling(other.model_price) if (isPerCall) { segments.push({ text: `${t('Per-call')} · ${formatBillingCurrencyFromUSD(other.model_price!, priceOpts)}`, }) } else if (other.model_ratio != null) { const inputPriceUSD = other.model_ratio * 2.0 const baseEntries = [formatPriceCompact(inputPriceUSD)] if (other.completion_ratio != null) { baseEntries.push( formatPriceCompact(inputPriceUSD * other.completion_ratio) ) } segments.push({ text: `${t('Standard')} · ${formatPriceList(baseEntries, true)}`, }) if (hasAnyCacheTokens(other)) { const cacheEntries = [ other.cache_ratio != null && other.cache_ratio !== 1 ? formatPriceCompact(inputPriceUSD * other.cache_ratio) : null, other.cache_creation_ratio != null && other.cache_creation_ratio !== 1 ? formatPriceCompact(inputPriceUSD * other.cache_creation_ratio) : null, other.cache_creation_ratio_1h != null && other.cache_creation_ratio_1h !== 0 ? formatPriceCompact(inputPriceUSD * other.cache_creation_ratio_1h) : null, ].filter(Boolean) as string[] if (cacheEntries.length > 0) { segments.push({ text: `${t('Cache')} ${formatPriceList(cacheEntries, false)}`, muted: true, }) } } } else { const userGroupRatio = other.user_group_ratio const groupRatio = other.group_ratio const isUserGroup = userGroupRatio != null && Number.isFinite(userGroupRatio) && userGroupRatio !== -1 const effectiveRatio = isUserGroup ? userGroupRatio : groupRatio const ratioLabel = isUserGroup ? t('User Exclusive Ratio') : t('Group Ratio') if (effectiveRatio != null && Number.isFinite(effectiveRatio)) { segments.push({ text: `${ratioLabel} ${formatRatioCompact(effectiveRatio)}x`, }) } } } if (other.is_system_prompt_overwritten) { segments.push({ text: t('System Prompt Override'), danger: true, }) } return segments } export function useCommonLogsColumns(isAdmin: boolean): ColumnDef[] { const { t } = useTranslation() const columns: ColumnDef[] = [ { accessorKey: 'created_at', header: ({ column }) => ( ), cell: ({ row }) => { const log = row.original const timestamp = row.getValue('created_at') as number const config = getLogTypeConfig(log.type) return (
{formatTimestampToDate(timestamp)}
) }, filterFn: (row, _id, value) => { if (!value || value.length === 0) return true return value.includes(String(row.original.type)) }, enableHiding: false, meta: { label: t('Time') }, }, ] if (isAdmin) { columns.push( { id: 'channel', header: ({ column }) => ( ), cell: function ChannelCell({ row }) { const { sensitiveVisible, setAffinityTarget, setAffinityDialogOpen, } = useUsageLogsContext() const log = row.original if (!isDisplayableLogType(log.type)) return null const other = parseLogOther(log.other) const affinity = other?.admin_info?.channel_affinity const useChannel = other?.admin_info?.use_channel const channelChain = useChannel && useChannel.length > 0 ? useChannel.join(' → ') : undefined const channelDisplay = log.channel_name ? `${log.channel_name} #${log.channel}` : `#${log.channel}` const channelIdDisplay = `#${log.channel}` const channelName = sensitiveVisible ? log.channel_name : '••••' return (
{affinity && ( )}
{log.channel_name && ( {channelName} )}

{sensitiveVisible ? channelDisplay : channelIdDisplay}

{channelChain && (

{t('Chain')}: {channelChain}

)} {affinity && (

{t('Channel Affinity')}

{t('Rule')}: {affinity.rule_name || '-'}

{t('Group')}:{' '} {sensitiveVisible ? affinity.using_group || affinity.selected_group || '-' : '••••'}

)}
) }, meta: { label: t('Channel'), mobileHidden: true }, }, { id: 'user', header: ({ column }) => ( ), cell: function UserCell({ row }) { const { sensitiveVisible, setSelectedUserId, setUserInfoDialogOpen, } = useUsageLogsContext() const log = row.original if (!log.username) return null return ( ) }, meta: { label: t('User'), mobileHidden: true }, } ) } columns.push({ accessorKey: 'token_name', header: ({ column }) => ( ), cell: function TokenNameCell({ row }) { const { sensitiveVisible } = useUsageLogsContext() const log = row.original if (!isDisplayableLogType(log.type)) return null const tokenName = log.token_name if (!tokenName) return null const displayName = sensitiveVisible ? tokenName : '••••' return (
) }, meta: { label: t('Token') }, size: 130, }) columns.push( { accessorKey: 'model_name', header: ({ column }) => ( ), cell: function ModelCell({ row }) { const { sensitiveVisible } = useUsageLogsContext() const log = row.original if (!isDisplayableLogType(log.type)) return null const modelInfo = formatModelName(log) const other = parseLogOther(log.other) let group = log.group if (!group) group = other?.group || '' const badgeClass = 'truncate rounded-md border border-border/60 bg-muted/30 px-1.5 py-0.5 font-mono' const modelBadge = modelInfo.isMapped ? (
{t('Request Model:')} {modelInfo.name}
{t('Actual Model:')} {modelInfo.actualModel}
) : ( ) const metaParts: string[] = [] if (group) metaParts.push(sensitiveVisible ? group : '••••') return (
{modelBadge} {metaParts.length > 0 && ( {metaParts.join(' · ')} )}
) }, meta: { label: t('Model'), mobileTitle: true }, }, { accessorKey: 'use_time', header: ({ column }) => ( ), cell: ({ row }) => { const log = row.original if (!isTimingLogType(log.type)) return null const useTime = row.getValue('use_time') as number const other = parseLogOther(log.other) const frt = other?.frt const timeVariant = getTimeColor(useTime) const frtVariant = frt ? getTimeColor(frt / 1000) : null const pillBg: Record = { success: 'border border-emerald-200/60 bg-emerald-50/50 dark:border-emerald-800/50 dark:bg-emerald-950/20', info: 'border border-sky-200/60 bg-sky-50/50 dark:border-sky-800/50 dark:bg-sky-950/20', warning: 'border border-amber-200/60 bg-amber-50/50 dark:border-amber-800/50 dark:bg-amber-950/20', } return (
{log.is_stream && (frt != null && frt > 0 ? ( {formatUseTime(frt / 1000)} ) : ( N/A ))}
{log.is_stream ? t('Stream') : t('Non-stream')} {useTime > 0 && (log.prompt_tokens + log.completion_tokens) > 0 && ( <> {' · '} {Math.round( (log.is_stream ? log.completion_tokens : log.prompt_tokens + log.completion_tokens) / useTime )} {' t/s'} )} {log.is_stream && other?.stream_status && other.stream_status.status !== 'ok' && (

{t('Stream Status')}: {t('Error')}

{other.stream_status.end_reason || 'unknown'}

{(other.stream_status.error_count ?? 0) > 0 && (

{t('Soft Errors')}:{' '} {other.stream_status.error_count}

)}
)}
) }, meta: { label: t('Timing'), mobileHidden: true }, }, { accessorKey: 'prompt_tokens', header: ({ column }) => ( ), cell: ({ row }) => { const log = row.original if (!isDisplayableLogType(log.type)) return null const other = parseLogOther(log.other) const promptTokens = log.prompt_tokens || 0 const completionTokens = log.completion_tokens || 0 if (promptTokens === 0 && completionTokens === 0) { return - } const cacheReadTokens = other?.cache_tokens || 0 const cacheWrite5m = other?.cache_creation_tokens_5m || 0 const cacheWrite1h = other?.cache_creation_tokens_1h || 0 const hasSplitCache = cacheWrite5m > 0 || cacheWrite1h > 0 const cacheWriteTokens = hasSplitCache ? cacheWrite5m + cacheWrite1h : other?.cache_creation_tokens || 0 return (
{promptTokens.toLocaleString()} / {completionTokens.toLocaleString()} {(cacheReadTokens > 0 || cacheWriteTokens > 0) && (
{cacheReadTokens > 0 && ( {t('Cache')}↓ {cacheReadTokens.toLocaleString()} )} {cacheWriteTokens > 0 && ( ↑ {cacheWriteTokens.toLocaleString()} )}
)}
) }, meta: { label: 'Tokens', mobileHidden: true }, }, { accessorKey: 'quota', header: ({ column }) => ( ), cell: ({ row }) => { const log = row.original if (!isDisplayableLogType(log.type)) return null const quota = row.getValue('quota') as number const other = parseLogOther(log.other) const isSubscription = other?.billing_source === 'subscription' if (isSubscription) { return ( {t('Deducted by subscription')}: {formatLogQuota(quota)} ) } const quotaStr = formatLogQuota(quota) return (
{quotaStr} {(() => { const userGroupRatio = other?.user_group_ratio if ( userGroupRatio != null && userGroupRatio !== -1 && Number.isFinite(userGroupRatio) ) { return ( {t('User Group: {{ratio}}x', { ratio: userGroupRatio })} ) } const groupRatio = other?.group_ratio if (groupRatio != null && groupRatio !== 1) { return ( {t('Group: {{ratio}}x', { ratio: groupRatio })} ) } return null })()}
) }, meta: { label: t('Cost') }, }, { accessorKey: 'content', header: t('Details'), cell: function DetailsCell({ row }) { const [dialogOpen, setDialogOpen] = useState(false) const log = row.original const other = parseLogOther(log.other) const segments = buildDetailSegments(log, other, t) const primary = segments[0] const hasMore = segments.length > 1 return ( <> ) }, meta: { label: t('Details'), mobileHidden: true }, size: 180, maxSize: 200, } ) return columns }