/*
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 { useNavigate, useParams, useSearch } from '@tanstack/react-router'
import {
ArrowLeft,
CalendarClock,
Code2,
FileText,
HeartPulse,
Info,
Layers,
Maximize2,
Sparkles,
} from 'lucide-react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { CopyButton } from '@/components/copy-button'
import { StaticDataTable } from '@/components/data-table'
import { Button } from '@/components/design-system/button'
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/components/design-system/tabs'
import { GroupBadge } from '@/components/group-badge'
import { PublicLayout } from '@/components/layout'
import { PageTransition } from '@/components/page-transition'
import { StatusBadge } from '@/components/status-badge'
import { Skeleton } from '@/components/ui/skeleton'
import { getPerfMetrics } from '@/features/performance-metrics/api'
import {
formatLatency,
formatThroughput,
formatUptimePct,
getSuccessRateTextClass,
} from '@/features/performance-metrics/lib/format'
import { getLobeIcon } from '@/lib/lobe-icon'
import { cn } from '@/lib/utils'
import { DEFAULT_TOKEN_UNIT, QUOTA_TYPE_VALUES } from '../constants'
import { usePricingData } from '../hooks/use-pricing-data'
import {
getDynamicPriceEntries,
getDynamicPricingSummary,
getDynamicPricingTiers,
isDynamicPricingModel,
} from '../lib/dynamic-price'
import { parseTags } from '../lib/filters'
import { getAvailableGroups, isTokenBasedModel } from '../lib/model-helpers'
import { formatFixedPrice, formatGroupPrice } from '../lib/price'
import type {
ModelCapability,
PriceType,
PricingModel,
TokenUnit,
} from '../types'
import { DynamicPricingBreakdown } from './dynamic-pricing-breakdown'
import { ModelDetailsApi } from './model-details-api'
import { ModelDetailsPerformance } from './model-details-performance'
// ----------------------------------------------------------------------------
// Local UI helpers
// ----------------------------------------------------------------------------
function SectionTitle(props: {
children: React.ReactNode
description?: string
}) {
return (
{props.children}
{props.description && (
{props.description}
)}
)
}
const CAPABILITY_LABEL_KEYS: Record = {
function_calling: 'Function calling',
streaming: 'Streaming',
vision: 'Vision',
json_mode: 'JSON mode',
structured_output: 'Structured output',
reasoning: 'Reasoning',
tools: 'Tools',
system_prompt: 'System prompt',
web_search: 'Web search',
code_interpreter: 'Code interpreter',
caching: 'Prompt caching',
embeddings: 'Embeddings',
}
const MODALITY_LABEL_KEYS: Record = {
text: 'Text',
image: 'Image',
audio: 'Audio',
video: 'Video',
file: 'File',
}
const TOKEN_FORMAT = new Intl.NumberFormat(undefined, {
maximumFractionDigits: 1,
})
function formatCatalogTokenCount(tokens: number): string {
if (!Number.isFinite(tokens) || tokens <= 0) return ''
if (tokens >= 1_000_000) {
return `${TOKEN_FORMAT.format(tokens / 1_000_000)}M`
}
if (tokens >= 1_000) {
return `${TOKEN_FORMAT.format(tokens / 1_000)}K`
}
return TOKEN_FORMAT.format(tokens)
}
function formatCatalogYearMonth(value?: string): string {
if (!value) return ''
const [yearStr, monthStr] = value.split('-')
const year = Number(yearStr)
const month = Number(monthStr)
if (!Number.isFinite(year) || !Number.isFinite(month)) return value
const date = new Date(Date.UTC(year, month - 1, 1))
return date.toLocaleString(undefined, { year: 'numeric', month: 'short' })
}
function normalizeCatalogItems(items?: readonly string[]): string[] {
if (!items) return []
return items.filter((item) => item.trim().length > 0)
}
function OverviewMetric(props: {
label: string
value: React.ReactNode
valueClassName?: string
}) {
return (
{props.label}
{props.value}
)
}
function OverviewSummaryGrid(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 = metricsQuery.data?.data.groups ?? []
const successRates = groups
.map((group) => group.success_rate)
.filter((rate) => Number.isFinite(rate))
const successRate =
successRates.length > 0
? successRates.reduce((sum, rate) => sum + rate, 0) / successRates.length
: Number.NaN
const tpsValues = groups
.map((group) => group.avg_tps)
.filter((value) => value > 0)
const avgTps =
tpsValues.length > 0
? tpsValues.reduce((sum, value) => sum + value, 0) / tpsValues.length
: 0
const latencyValues = groups
.map((group) => group.avg_latency_ms)
.filter((value) => value > 0)
const avgLatency =
latencyValues.length > 0
? Math.round(
latencyValues.reduce((sum, value) => sum + value, 0) /
latencyValues.length
)
: 0
return (
)
}
function CatalogPillList(props: { items: string[] }) {
return (
{props.items.map((item) => (
{item}
))}
)
}
function CatalogTextValue(props: { children: React.ReactNode }) {
return (
{props.children}
)
}
function CatalogInfoCell(props: { label: string; children: React.ReactNode }) {
return (
{props.label}
{props.children}
)
}
function ModalityLabels(props: { items: string[] }) {
const { t } = useTranslation()
if (props.items.length === 0) return null
return (
{props.items.map((item) => (
{t(MODALITY_LABEL_KEYS[item] ?? item)}
))}
)
}
function ModelBackendQuickStats(props: { model: PricingModel }) {
const { t } = useTranslation()
const model = props.model
const inputModalities = normalizeCatalogItems(model.input_modalities)
const outputModalities = normalizeCatalogItems(model.output_modalities)
const contextLength = model.context_length ?? 0
const maxOutput = model.max_output_tokens ?? 0
const knowledgeCutoff = formatCatalogYearMonth(model.knowledge_cutoff)
const releaseDate = formatCatalogYearMonth(model.release_date)
const stats: {
key: string
icon: React.ComponentType<{ className?: string }>
label: string
value: React.ReactNode
hint?: string
}[] = []
if (contextLength > 0) {
stats.push({
key: 'context',
icon: Layers,
label: t('Context'),
value: formatCatalogTokenCount(contextLength),
hint: t('Maximum input window'),
})
}
if (maxOutput > 0) {
stats.push({
key: 'max-output',
icon: Maximize2,
label: t('Max output'),
value: formatCatalogTokenCount(maxOutput),
hint: t('Maximum tokens per response'),
})
}
if (inputModalities.length > 0 || outputModalities.length > 0) {
stats.push({
key: 'modalities',
icon: FileText,
label: t('Modalities'),
value: (
{inputModalities.length > 0 && outputModalities.length > 0 && (
โ
)}
),
})
}
if (knowledgeCutoff) {
stats.push({
key: 'knowledge',
icon: Sparkles,
label: t('Knowledge cutoff'),
value: knowledgeCutoff,
})
}
if (releaseDate) {
stats.push({
key: 'release',
icon: CalendarClock,
label: t('Released'),
value: releaseDate,
})
}
if (stats.length === 0) return null
return (
{stats.map((stat) => {
const Icon = stat.icon
return (
{stat.label}
{stat.value}
{stat.hint && (
{stat.hint}
)}
)
})}
)
}
function ModelBackendSignalsSection(props: { model: PricingModel }) {
const { t } = useTranslation()
const capabilities = normalizeCatalogItems(props.model.capabilities)
const inputModalities = normalizeCatalogItems(props.model.input_modalities)
const outputModalities = normalizeCatalogItems(props.model.output_modalities)
if (
capabilities.length === 0 &&
inputModalities.length === 0 &&
outputModalities.length === 0
) {
return null
}
return (
{t('Capabilities')}
{capabilities.length > 0 && (
t(
CAPABILITY_LABEL_KEYS[capability as ModelCapability] ??
capability
)
)}
/>
)}
{(inputModalities.length > 0 || outputModalities.length > 0) && (
{inputModalities.length > 0 && (
{t('Input')}
)}
{outputModalities.length > 0 && (
{t('Output')}
)}
)}
)
}
function ModelBackendProviderSection(props: { model: PricingModel }) {
const { t } = useTranslation()
const model = props.model
const groups = normalizeCatalogItems(model.enable_groups)
const endpoints = normalizeCatalogItems(model.supported_endpoint_types)
const tags = parseTags(model.tags)
const cells: React.ReactNode[] = []
if (model.vendor_name) {
cells.push(
{model.vendor_name}
)
}
cells.push(
{model.quota_type === QUOTA_TYPE_VALUES.TOKEN
? t('Token-based')
: t('Per Request')}
)
if (groups.length > 0) {
cells.push(
{groups.map((group) => (
))}
)
}
if (endpoints.length > 0) {
cells.push(
)
}
if (tags.length > 0) {
cells.push(
)
}
if (model.parameter_count) {
cells.push(
{model.parameter_count}
)
}
if (cells.length === 0) return null
return (
)
}
function ModelBackendDetailsSection(props: { model: PricingModel }) {
return (
<>
>
)
}
// ----------------------------------------------------------------------------
// Model header (always visible above the detail sections)
// ----------------------------------------------------------------------------
function ModelHeader(props: { model: PricingModel }) {
const { t } = useTranslation()
const model = props.model
const modelIconKey = model.icon || model.vendor_icon
const modelIcon = modelIconKey ? getLobeIcon(modelIconKey, 28) : null
const description = model.description || model.vendor_description || null
const tags = parseTags(model.tags)
const endpoints = normalizeCatalogItems(model.supported_endpoint_types)
const isSpecialExpression =
model.billing_mode === 'tiered_expr' &&
Boolean(model.billing_expr) &&
getDynamicPricingTiers(model).length === 0
return (
)
}
// ----------------------------------------------------------------------------
// Base price card (used in the Overview tab)
// ----------------------------------------------------------------------------
function PriceSection(props: {
model: PricingModel
priceRate: number
usdExchangeRate: number
tokenUnit: TokenUnit
showRechargePrice: boolean
}) {
const { t } = useTranslation()
const isTokenBased = isTokenBasedModel(props.model)
const tokenUnitLabel = props.tokenUnit === 'K' ? '1K' : '1M'
const baseGroupKey = '_base'
const baseGroupRatioMap = { [baseGroupKey]: 1 }
const dynamicSummary = getDynamicPricingSummary(props.model, {
tokenUnit: props.tokenUnit,
showRechargePrice: props.showRechargePrice,
priceRate: props.priceRate,
usdExchangeRate: props.usdExchangeRate,
groupRatioMultiplier: 1,
})
const primaryPriceTypes: { label: string; type: PriceType }[] = [
{ label: t('Input'), type: 'input' },
{ label: t('Output'), type: 'output' },
]
const secondaryPriceTypes: {
label: string
type: PriceType
available: boolean
}[] = [
{
label: t('Cached input'),
type: 'cache',
available: props.model.cache_ratio != null,
},
{
label: t('Cache write'),
type: 'create_cache',
available: props.model.create_cache_ratio != null,
},
{
label: t('Image input'),
type: 'image',
available: props.model.image_ratio != null,
},
{
label: t('Audio input'),
type: 'audio_input',
available: props.model.audio_ratio != null,
},
{
label: t('Audio output'),
type: 'audio_output',
available:
props.model.audio_ratio != null &&
props.model.audio_completion_ratio != null,
},
]
if (dynamicSummary) {
if (dynamicSummary.isSpecialExpression) {
return (
{t('Pricing')}
{t('Special billing expression')}
{t('Unable to parse structured pricing')}
{t('Raw expression')}
{dynamicSummary.rawExpression}
)
}
const priceRows = [
...dynamicSummary.primaryEntries,
...dynamicSummary.secondaryEntries,
]
return (
{t('Pricing')}
{t('Text tokens')}
{t('Prices shown per')} {tokenUnitLabel} {t('tokens')}
{priceRows.map((entry) => (
{t(entry.shortLabel)}
{entry.formatted}
))}
)
}
if (!isTokenBased) {
return (
{t('Pricing')}
{t('Per request')}
{formatFixedPrice(
props.model,
baseGroupKey,
props.showRechargePrice,
props.priceRate,
props.usdExchangeRate,
baseGroupRatioMap
)}
)
}
const secondaryItems = secondaryPriceTypes.filter((p) => p.available)
const priceRows = [
...primaryPriceTypes,
...secondaryItems.map((item) => ({
label: item.label,
type: item.type,
})),
]
return (
{t('Pricing')}
{t('Text tokens')}
{t('Prices shown per')} {tokenUnitLabel} {t('tokens')}
{priceRows.map((item) => (
{item.label}
{formatGroupPrice(
props.model,
baseGroupKey,
item.type,
props.tokenUnit,
props.showRechargePrice,
props.priceRate,
props.usdExchangeRate,
baseGroupRatioMap
)}
))}
)
}
// ----------------------------------------------------------------------------
// Auto group chain (used inside group pricing section)
// ----------------------------------------------------------------------------
function AutoGroupChain(props: { model: PricingModel; autoGroups: string[] }) {
const { t } = useTranslation()
const modelEnableGroups = Array.isArray(props.model.enable_groups)
? props.model.enable_groups
: []
const autoChain = props.autoGroups.filter((g) =>
modelEnableGroups.includes(g)
)
if (autoChain.length === 0) return null
return (
{t('Auto Group Chain')}
โ
{autoChain.map((g, idx) => (
{idx < autoChain.length - 1 && (
โ
)}
))}
)
}
type DynamicPriceOptions = Parameters[1]
type DynamicPricingTier = ReturnType[number]
type DynamicFormattedPricesByTier = Map>
function getDynamicPriceFields(
tiers: DynamicPricingTier[],
options: DynamicPriceOptions
) {
return [
...new Map(
tiers
.flatMap((tier) => getDynamicPriceEntries(tier, options))
.map((entry) => [entry.field, entry])
).values(),
]
}
function getDynamicFormattedPricesByTier(
tiers: DynamicPricingTier[],
options: DynamicPriceOptions
): DynamicFormattedPricesByTier {
return new Map(
tiers.map((tier) => [
tier,
new Map(
getDynamicPriceEntries(tier, options).map((entry) => [
entry.field,
entry.formatted,
])
),
])
)
}
// ----------------------------------------------------------------------------
// Group pricing table
// ----------------------------------------------------------------------------
function GroupPricingSection(props: {
model: PricingModel
groupRatio: Record
usableGroup: Record
autoGroups: string[]
priceRate: number
usdExchangeRate: number
tokenUnit: TokenUnit
showRechargePrice?: boolean
}) {
const { t } = useTranslation()
const showRechargePrice = props.showRechargePrice ?? false
const availableGroups = useMemo(
() => getAvailableGroups(props.model, props.usableGroup || {}),
[props.model, props.usableGroup]
)
const isTokenBased = isTokenBasedModel(props.model)
const tokenUnitLabel = props.tokenUnit === 'K' ? '1K' : '1M'
const extraPriceTypes = useMemo(() => {
const types: { label: string; type: PriceType }[] = []
if (props.model.cache_ratio != null) {
types.push({ label: t('Cache'), type: 'cache' })
}
if (props.model.create_cache_ratio != null) {
types.push({ label: t('Cache Write'), type: 'create_cache' })
}
if (props.model.image_ratio != null) {
types.push({ label: t('Image'), type: 'image' })
}
if (props.model.audio_ratio != null) {
types.push({ label: t('Audio In'), type: 'audio_input' })
}
if (
props.model.audio_ratio != null &&
props.model.audio_completion_ratio != null
) {
types.push({ label: t('Audio Out'), type: 'audio_output' })
}
return types
}, [props.model, t])
if (availableGroups.length === 0) {
return (
{t('Pricing by Group')}
{t(
'This model is not available in any group, or no group pricing information is configured.'
)}
)
}
const thClass = 'text-muted-foreground py-2 text-xs font-medium'
if (isDynamicPricingModel(props.model)) {
const dynamicTiers = getDynamicPricingTiers(props.model)
if (dynamicTiers.length === 0) {
return (
{t('Pricing by Group')}
{t('Special billing expression')}
{t(
'Group prices cannot be expanded because this expression is not a standard tiered pricing expression.'
)}
{t('Raw expression')}
{props.model.billing_expr}
)
}
const priceFields = getDynamicPriceFields(dynamicTiers, {
tokenUnit: props.tokenUnit,
showRechargePrice,
priceRate: props.priceRate,
usdExchangeRate: props.usdExchangeRate,
groupRatioMultiplier: 1,
})
const formattedPricesByGroup = new Map(
availableGroups.map((group) => {
const ratio = props.groupRatio[group] || 1
return [
group,
getDynamicFormattedPricesByTier(dynamicTiers, {
tokenUnit: props.tokenUnit,
showRechargePrice,
priceRate: props.priceRate,
usdExchangeRate: props.usdExchangeRate,
groupRatioMultiplier: ratio,
}),
] as const
})
)
return (
{t('Pricing by Group')}
{availableGroups.map((group) => {
const ratio = props.groupRatio[group] || 1
const formattedPricesByTier =
formattedPricesByGroup.get(group) ??
new Map
>()
return (
{ratio}x
`${group}-${tier.label || tierIndex}`
}
columns={[
{
id: 'tier',
header: t('Tier'),
className: thClass,
cellClassName: 'text-muted-foreground py-2.5',
cell: (tier) => tier.label || t('Default'),
},
...priceFields.map((fieldEntry) => ({
id: fieldEntry.field,
header: t(fieldEntry.shortLabel),
className: `${thClass} text-right`,
cellClassName: 'py-2.5 text-right font-mono',
cell: (tier: (typeof dynamicTiers)[number]) =>
formattedPricesByTier
.get(tier)
?.get(fieldEntry.field) ?? '-',
})),
]}
/>
)
})}
{t('Prices shown per')} {tokenUnitLabel} tokens
)
}
const renderGroupPrice = (group: string, type: PriceType) =>
formatGroupPrice(
props.model,
group,
type,
props.tokenUnit,
showRechargePrice,
props.priceRate,
props.usdExchangeRate,
props.groupRatio
)
const renderFixedGroupPrice = (group: string) =>
formatFixedPrice(
props.model,
group,
showRechargePrice,
props.priceRate,
props.usdExchangeRate,
props.groupRatio
)
return (
{t('Pricing by Group')}
group}
columns={[
{
id: 'group',
header: t('Group'),
className: thClass,
cellClassName: 'py-2.5',
cell: (group) => ,
},
{
id: 'ratio',
header: t('Ratio'),
className: thClass,
cellClassName: 'text-muted-foreground py-2.5 font-mono',
cell: (group) => `${props.groupRatio[group] || 1}x`,
},
...(isTokenBased
? [
{
id: 'input',
header: t('Input'),
className: `${thClass} text-right`,
cellClassName: 'py-2.5 text-right font-mono',
cell: (group: string) => renderGroupPrice(group, 'input'),
},
{
id: 'output',
header: t('Output'),
className: `${thClass} text-right`,
cellClassName: 'py-2.5 text-right font-mono',
cell: (group: string) => renderGroupPrice(group, 'output'),
},
...extraPriceTypes.map((ep) => ({
id: ep.type,
header: ep.label,
className: `${thClass} text-right`,
cellClassName: 'py-2.5 text-right font-mono',
cell: (group: string) => renderGroupPrice(group, ep.type),
})),
]
: [
{
id: 'price',
header: t('Price'),
className: `${thClass} text-right`,
cellClassName: 'py-2.5 text-right font-mono',
cell: renderFixedGroupPrice,
},
]),
]}
/>
{isTokenBased && (
{t('Prices shown per')} {tokenUnitLabel} tokens
)}
)
}
const TAB_VALUES = ['overview', 'performance', 'api'] as const
type TabValue = (typeof TAB_VALUES)[number]
const TAB_META: Record<
TabValue,
{ icon: React.ComponentType<{ className?: string }>; labelKey: string }
> = {
overview: { icon: Info, labelKey: 'Overview' },
performance: { icon: HeartPulse, labelKey: 'Performance' },
api: { icon: Code2, labelKey: 'API' },
}
export interface ModelDetailsContentProps {
model: PricingModel
groupRatio: Record
usableGroup: Record
endpointMap: Record
autoGroups: string[]
priceRate: number
usdExchangeRate: number
tokenUnit: TokenUnit
showRechargePrice?: boolean
}
export function ModelDetailsContent(props: ModelDetailsContentProps) {
const { t } = useTranslation()
const showRechargePrice = props.showRechargePrice ?? false
const isDynamic =
props.model.billing_mode === 'tiered_expr' &&
Boolean(props.model.billing_expr)
return (
{TAB_VALUES.map((value) => {
const Icon = TAB_META[value].icon
return (
{t(TAB_META[value].labelKey)}
)
})}
{isDynamic && (
)}
)
}
export function ModelDetails() {
const { t } = useTranslation()
const { modelId } = useParams({ from: '/pricing/$modelId/' })
const search = useSearch({ from: '/pricing/$modelId/' })
const navigate = useNavigate()
const {
models,
groupRatio,
usableGroup,
endpointMap,
autoGroups,
isLoading,
priceRate,
usdExchangeRate,
} = usePricingData()
const tokenUnit: TokenUnit =
search.tokenUnit === 'K' ? 'K' : DEFAULT_TOKEN_UNIT
const model = useMemo(() => {
if (!models || !modelId) return null
return models.find((m) => m.model_name === modelId) || null
}, [models, modelId])
const handleBack = () => {
navigate({ to: '/pricing', search })
}
if (isLoading) {
return (
{['stats-a', 'stats-b', 'stats-c', 'stats-d'].map((key) => (
))}
{['block-a', 'block-b', 'block-c', 'block-d'].map((key) => (
))}
)
}
if (!model) {
return (
{t('Model not found')}
{t("The model you're looking for doesn't exist.")}
{t('Back to Models')}
)
}
return (
{t('Back')}
) || {}
}
/>
)
}