diff --git a/web/default/src/features/pricing/components/model-card-grid.tsx b/web/default/src/features/pricing/components/model-card-grid.tsx
index 9faf91b5..d92593ab 100644
--- a/web/default/src/features/pricing/components/model-card-grid.tsx
+++ b/web/default/src/features/pricing/components/model-card-grid.tsx
@@ -36,6 +36,7 @@ export interface ModelCardGridProps {
usdExchangeRate?: number
tokenUnit?: TokenUnit
showRechargePrice?: boolean
+ selectedGroup?: string
}
export function ModelCardGrid(props: ModelCardGridProps) {
@@ -81,6 +82,7 @@ export function ModelCardGrid(props: ModelCardGridProps) {
priceRate={props.priceRate}
usdExchangeRate={props.usdExchangeRate}
showRechargePrice={props.showRechargePrice}
+ selectedGroup={props.selectedGroup}
perf={perfMap.get(model.model_name || '')}
onClick={() => props.onModelClick(model.model_name || '')}
/>
diff --git a/web/default/src/features/pricing/components/model-card.tsx b/web/default/src/features/pricing/components/model-card.tsx
index ef08e9ce..4e25eace 100644
--- a/web/default/src/features/pricing/components/model-card.tsx
+++ b/web/default/src/features/pricing/components/model-card.tsx
@@ -17,7 +17,7 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import { ChevronRight, Copy } from 'lucide-react'
-import { memo } from 'react'
+import { memo, type ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import { StatusBadge } from '@/components/status-badge'
@@ -43,6 +43,7 @@ export interface ModelCardProps {
usdExchangeRate?: number
tokenUnit?: TokenUnit
showRechargePrice?: boolean
+ selectedGroup?: string
perf?: ModelPerfBadgeData
}
@@ -71,7 +72,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
showRechargePrice,
priceRate,
usdExchangeRate,
- groupRatioMultiplier: getDynamicDisplayGroupRatio(props.model),
+ groupRatioMultiplier: getDynamicDisplayGroupRatio(
+ props.model,
+ props.selectedGroup
+ ),
})
: null
@@ -87,6 +91,111 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
copyToClipboard(props.model.model_name || '')
}
+ let priceSummary: ReactNode
+ if (dynamicSummary) {
+ if (dynamicSummary.isSpecialExpression) {
+ priceSummary = (
+
+
+ {t('Special billing expression')}
+
+
+ {dynamicSummary.rawExpression}
+
+
+ )
+ } else if (dynamicSummary.primaryEntries.length > 0) {
+ priceSummary = (
+ <>
+ {dynamicSummary.primaryEntries.map((entry) => (
+
+ {t(entry.shortLabel)}{' '}
+
+ {entry.formatted}
+
+ /{tokenUnitLabel}
+
+ ))}
+ >
+ )
+ } else {
+ priceSummary = (
+
+ {t('Dynamic Pricing')}
+
+ )
+ }
+ } else if (isTokenBased) {
+ priceSummary = (
+ <>
+
+ {t('Input')}{' '}
+
+ {formatPrice(
+ props.model,
+ 'input',
+ tokenUnit,
+ showRechargePrice,
+ priceRate,
+ usdExchangeRate,
+ props.selectedGroup
+ )}
+
+ /{tokenUnitLabel}
+
+
+ {t('Output')}{' '}
+
+ {formatPrice(
+ props.model,
+ 'output',
+ tokenUnit,
+ showRechargePrice,
+ priceRate,
+ usdExchangeRate,
+ props.selectedGroup
+ )}
+
+ /{tokenUnitLabel}
+
+ {hasCachedPrice && (
+
+ {t('Cached')}{' '}
+
+ {formatPrice(
+ props.model,
+ 'cache',
+ tokenUnit,
+ showRechargePrice,
+ priceRate,
+ usdExchangeRate,
+ props.selectedGroup
+ )}
+
+
+ )}
+ >
+ )
+ } else {
+ priceSummary = (
+
+
+ {formatRequestPrice(
+ props.model,
+ showRechargePrice,
+ priceRate,
+ usdExchangeRate,
+ props.selectedGroup
+ )}
+ {' '}
+ / {t('request')}
+
+ )
+ }
+
return (
- {dynamicSummary ? (
- dynamicSummary.isSpecialExpression ? (
-
-
- {t('Special billing expression')}
-
-
- {dynamicSummary.rawExpression}
-
-
- ) : dynamicSummary.primaryEntries.length > 0 ? (
- <>
- {dynamicSummary.primaryEntries.map((entry) => (
-
- {t(entry.shortLabel)}{' '}
-
- {entry.formatted}
-
- /{tokenUnitLabel}
-
- ))}
- >
- ) : (
-
- {t('Dynamic Pricing')}
-
- )
- ) : isTokenBased ? (
- <>
-
- {t('Input')}{' '}
-
- {formatPrice(
- props.model,
- 'input',
- tokenUnit,
- showRechargePrice,
- priceRate,
- usdExchangeRate
- )}
-
- /{tokenUnitLabel}
-
-
- {t('Output')}{' '}
-
- {formatPrice(
- props.model,
- 'output',
- tokenUnit,
- showRechargePrice,
- priceRate,
- usdExchangeRate
- )}
-
- /{tokenUnitLabel}
-
- {hasCachedPrice && (
-
- {t('Cached')}{' '}
-
- {formatPrice(
- props.model,
- 'cache',
- tokenUnit,
- showRechargePrice,
- priceRate,
- usdExchangeRate
- )}
-
-
- )}
- >
- ) : (
-
-
- {formatRequestPrice(
- props.model,
- showRechargePrice,
- priceRate,
- usdExchangeRate
- )}
- {' '}
- / {t('request')}
-
- )}
+ {priceSummary}
diff --git a/web/default/src/features/pricing/components/pricing-columns.tsx b/web/default/src/features/pricing/components/pricing-columns.tsx
index c999f3cf..b298e6d9 100644
--- a/web/default/src/features/pricing/components/pricing-columns.tsx
+++ b/web/default/src/features/pricing/components/pricing-columns.tsx
@@ -16,7 +16,7 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import { type ColumnDef } from '@tanstack/react-table'
+import type { ColumnDef } from '@tanstack/react-table'
import { useTranslation } from 'react-i18next'
import {
@@ -51,6 +51,7 @@ export interface PricingColumnsOptions {
priceRate?: number
usdExchangeRate?: number
showRechargePrice?: boolean
+ selectedGroup?: string
}
export function usePricingColumns(
@@ -62,6 +63,7 @@ export function usePricingColumns(
priceRate = 1,
usdExchangeRate = 1,
showRechargePrice = false,
+ selectedGroup,
} = options
const tokenUnitLabel = tokenUnit === 'K' ? '1K' : '1M'
@@ -124,7 +126,10 @@ export function usePricingColumns(
showRechargePrice,
priceRate,
usdExchangeRate,
- groupRatioMultiplier: getDynamicDisplayGroupRatio(model),
+ groupRatioMultiplier: getDynamicDisplayGroupRatio(
+ model,
+ selectedGroup
+ ),
})
if (dynamicSummary) {
@@ -186,7 +191,8 @@ export function usePricingColumns(
tokenUnit,
showRechargePrice,
priceRate,
- usdExchangeRate
+ usdExchangeRate,
+ selectedGroup
)
)
const outputPrice = stripTrailingZeros(
@@ -196,7 +202,8 @@ export function usePricingColumns(
tokenUnit,
showRechargePrice,
priceRate,
- usdExchangeRate
+ usdExchangeRate,
+ selectedGroup
)
)
@@ -219,7 +226,8 @@ export function usePricingColumns(
model,
showRechargePrice,
priceRate,
- usdExchangeRate
+ usdExchangeRate,
+ selectedGroup
)
)
@@ -247,7 +255,10 @@ export function usePricingColumns(
showRechargePrice,
priceRate,
usdExchangeRate,
- groupRatioMultiplier: getDynamicDisplayGroupRatio(model),
+ groupRatioMultiplier: getDynamicDisplayGroupRatio(
+ model,
+ selectedGroup
+ ),
})
if (dynamicSummary) {
@@ -291,7 +302,8 @@ export function usePricingColumns(
tokenUnit,
showRechargePrice,
priceRate,
- usdExchangeRate
+ usdExchangeRate,
+ selectedGroup
)
)
diff --git a/web/default/src/features/pricing/components/pricing-table.tsx b/web/default/src/features/pricing/components/pricing-table.tsx
index e6aa9d36..7718b29b 100644
--- a/web/default/src/features/pricing/components/pricing-table.tsx
+++ b/web/default/src/features/pricing/components/pricing-table.tsx
@@ -16,7 +16,7 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import { type Row, type PaginationState } from '@tanstack/react-table'
+import type { Row, PaginationState } from '@tanstack/react-table'
import { useState, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@@ -38,6 +38,7 @@ export interface PricingTableProps {
usdExchangeRate?: number
tokenUnit?: TokenUnit
showRechargePrice?: boolean
+ selectedGroup?: string
onModelClick?: (modelName: string) => void
}
@@ -50,6 +51,7 @@ export function PricingTable(props: PricingTableProps) {
usdExchangeRate = 1,
tokenUnit = DEFAULT_TOKEN_UNIT,
showRechargePrice = false,
+ selectedGroup,
onModelClick,
} = props
@@ -63,6 +65,7 @@ export function PricingTable(props: PricingTableProps) {
priceRate,
usdExchangeRate,
showRechargePrice,
+ selectedGroup,
})
const { table } = useDataTable({
diff --git a/web/default/src/features/pricing/index.tsx b/web/default/src/features/pricing/index.tsx
index f1019de0..7b98d343 100644
--- a/web/default/src/features/pricing/index.tsx
+++ b/web/default/src/features/pricing/index.tsx
@@ -130,6 +130,7 @@ export function Pricing() {
usdExchangeRate={usdExchangeRate}
tokenUnit={tokenUnit}
showRechargePrice={showRechargePrice}
+ selectedGroup={groupFilter}
/>
)
}
@@ -141,6 +142,7 @@ export function Pricing() {
usdExchangeRate={usdExchangeRate}
tokenUnit={tokenUnit}
showRechargePrice={showRechargePrice}
+ selectedGroup={groupFilter}
onModelClick={handleModelClick}
/>
)
diff --git a/web/default/src/features/pricing/lib/dynamic-price.ts b/web/default/src/features/pricing/lib/dynamic-price.ts
index 581b443b..68160822 100644
--- a/web/default/src/features/pricing/lib/dynamic-price.ts
+++ b/web/default/src/features/pricing/lib/dynamic-price.ts
@@ -28,6 +28,7 @@ import {
type BillingVar,
type ParsedTier,
} from './billing-expr'
+import { getDisplayGroupRatio } from './model-helpers'
type DynamicPriceOptions = {
tokenUnit: TokenUnit
@@ -65,20 +66,11 @@ export function isDynamicPricingModel(model: PricingModel): boolean {
return model.billing_mode === 'tiered_expr' && Boolean(model.billing_expr)
}
-export function getDynamicDisplayGroupRatio(model: PricingModel): number {
- const groups = Array.isArray(model.enable_groups) ? model.enable_groups : []
- const ratios = model.group_ratio || {}
- if (groups.length === 0) return 1
-
- let minRatio = Number.POSITIVE_INFINITY
- for (const group of groups) {
- const ratio = ratios[group]
- if (ratio !== undefined && ratio < minRatio) {
- minRatio = ratio
- }
- }
-
- return minRatio === Number.POSITIVE_INFINITY ? 1 : minRatio
+export function getDynamicDisplayGroupRatio(
+ model: PricingModel,
+ selectedGroup?: string
+): number {
+ return getDisplayGroupRatio(model, selectedGroup)
}
function applyRechargeRate(
diff --git a/web/default/src/features/pricing/lib/model-helpers.ts b/web/default/src/features/pricing/lib/model-helpers.ts
index 126285a7..3a9d5b42 100644
--- a/web/default/src/features/pricing/lib/model-helpers.ts
+++ b/web/default/src/features/pricing/lib/model-helpers.ts
@@ -16,7 +16,7 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import { EXCLUDED_GROUPS, QUOTA_TYPE_VALUES } from '../constants'
+import { EXCLUDED_GROUPS, FILTER_ALL, QUOTA_TYPE_VALUES } from '../constants'
import type { PricingModel } from '../types'
// ----------------------------------------------------------------------------
@@ -39,11 +39,66 @@ export function getAvailableGroups(
.filter((g) => modelEnableGroups.includes(g))
}
+/**
+ * Read a configured group ratio while preserving valid zero ratios.
+ */
+export function getConfiguredGroupRatio(
+ groupRatio: Record,
+ group: string
+): number {
+ const ratio = groupRatio[group]
+ return typeof ratio === 'number' && Number.isFinite(ratio) ? ratio : 1
+}
+
+/**
+ * Resolve the group ratio used by model square summary prices.
+ *
+ * When no specific group is selected, the model square shows the best price
+ * available to the viewer. When a group filter is active, it mirrors classic
+ * and shows that group's price.
+ */
+export function getDisplayGroupRatio(
+ model: PricingModel,
+ selectedGroup?: string
+): number {
+ const modelEnableGroups = Array.isArray(model.enable_groups)
+ ? model.enable_groups
+ : []
+ const groupRatio = model.group_ratio || {}
+
+ if (
+ selectedGroup &&
+ selectedGroup !== FILTER_ALL &&
+ modelEnableGroups.includes(selectedGroup)
+ ) {
+ return getConfiguredGroupRatio(groupRatio, selectedGroup)
+ }
+
+ if (modelEnableGroups.length === 0) {
+ return 1
+ }
+
+ let minRatio = Number.POSITIVE_INFINITY
+
+ for (const group of modelEnableGroups) {
+ const ratio = groupRatio[group]
+ if (
+ typeof ratio === 'number' &&
+ Number.isFinite(ratio) &&
+ ratio < minRatio
+ ) {
+ minRatio = ratio
+ }
+ }
+
+ return minRatio === Number.POSITIVE_INFINITY ? 1 : minRatio
+}
+
/**
* Replace model placeholder in endpoint path
*/
export function replaceModelInPath(path: string, modelName: string): string {
- return path.replace(/\{model\}/g, modelName)
+ return path.replaceAll('{model}', modelName)
}
/**
diff --git a/web/default/src/features/pricing/lib/price.ts b/web/default/src/features/pricing/lib/price.ts
index 534ccdf3..1f36462b 100644
--- a/web/default/src/features/pricing/lib/price.ts
+++ b/web/default/src/features/pricing/lib/price.ts
@@ -20,6 +20,7 @@ import { formatCurrencyFromUSD } from '@/lib/currency'
import { QUOTA_TYPE_VALUES, TOKEN_UNIT_DIVISORS } from '../constants'
import type { PricingModel, TokenUnit, PriceType } from '../types'
+import { getConfiguredGroupRatio, getDisplayGroupRatio } from './model-helpers'
// ----------------------------------------------------------------------------
// Price Calculation Utilities
@@ -36,11 +37,11 @@ export function stripTrailingZeros(formatted: string): string {
const [, symbol, number, suffix] = match
// Remove commas for processing
- const cleanNumber = number.replace(/,/g, '')
+ const cleanNumber = number.replaceAll(',', '')
// Convert to number and back to remove trailing zeros
- const parsed = parseFloat(cleanNumber)
- if (isNaN(parsed)) return formatted
+ const parsed = Number.parseFloat(cleanNumber)
+ if (Number.isNaN(parsed)) return formatted
// Convert to string, which automatically removes trailing zeros
let result = parsed.toString()
@@ -53,27 +54,6 @@ export function stripTrailingZeros(formatted: string): string {
return `${symbol}${result}${suffix}`
}
-/**
- * Find minimum group ratio from enabled groups
- */
-function getMinGroupRatio(
- enableGroups: string[],
- groupRatio: Record
-): number {
- if (enableGroups.length === 0) return 1
-
- let minRatio = Number.POSITIVE_INFINITY
-
- for (const group of enableGroups) {
- const ratio = groupRatio[group]
- if (ratio !== undefined && ratio < minRatio) {
- minRatio = ratio
- }
- }
-
- return minRatio === Number.POSITIVE_INFINITY ? 1 : minRatio
-}
-
/**
* Calculate token price in USD.
*
@@ -95,26 +75,26 @@ function calculateTokenPrice(
case 'cache':
return hasRatio(model.cache_ratio)
? base * Number(model.cache_ratio)
- : NaN
+ : Number.NaN
case 'create_cache':
return hasRatio(model.create_cache_ratio)
? base * Number(model.create_cache_ratio)
- : NaN
+ : Number.NaN
case 'image':
return hasRatio(model.image_ratio)
? base * Number(model.image_ratio)
- : NaN
+ : Number.NaN
case 'audio_input':
return hasRatio(model.audio_ratio)
? base * Number(model.audio_ratio)
- : NaN
+ : Number.NaN
case 'audio_output':
return hasRatio(model.audio_ratio) &&
hasRatio(model.audio_completion_ratio)
? base *
Number(model.audio_ratio) *
Number(model.audio_completion_ratio)
- : NaN
+ : Number.NaN
}
}
@@ -167,19 +147,16 @@ export function formatPrice(
tokenUnit: TokenUnit,
showWithRecharge = false,
priceRate = 1,
- usdExchangeRate = 1
+ usdExchangeRate = 1,
+ selectedGroup?: string
): string {
if (model.quota_type === QUOTA_TYPE_VALUES.REQUEST) {
return '-'
}
- const enableGroups = Array.isArray(model.enable_groups)
- ? model.enable_groups
- : []
- const groupRatio = model.group_ratio || {}
- const minRatio = getMinGroupRatio(enableGroups, groupRatio)
+ const displayGroupRatio = getDisplayGroupRatio(model, selectedGroup)
- let priceInUSD = calculateTokenPrice(model, type, minRatio)
+ let priceInUSD = calculateTokenPrice(model, type, displayGroupRatio)
priceInUSD = applyRechargeRate(
priceInUSD,
showWithRecharge,
@@ -212,7 +189,7 @@ export function formatGroupPrice(
return '-'
}
- const ratio = groupRatio[group] || 1
+ const ratio = getConfiguredGroupRatio(groupRatio, group)
let priceInUSD = calculateTokenPrice(model, type, ratio)
priceInUSD = applyRechargeRate(
@@ -245,7 +222,7 @@ export function formatFixedPrice(
return '-'
}
- const ratio = groupRatio[group] || 1
+ const ratio = getConfiguredGroupRatio(groupRatio, group)
let priceInUSD = (model.model_price || 0) * ratio
priceInUSD = applyRechargeRate(
@@ -269,19 +246,16 @@ export function formatRequestPrice(
model: PricingModel,
showWithRecharge = false,
priceRate = 1,
- usdExchangeRate = 1
+ usdExchangeRate = 1,
+ selectedGroup?: string
): string {
if (model.quota_type !== QUOTA_TYPE_VALUES.REQUEST) {
return '-'
}
- const enableGroups = Array.isArray(model.enable_groups)
- ? model.enable_groups
- : []
- const groupRatio = model.group_ratio || {}
- const minRatio = getMinGroupRatio(enableGroups, groupRatio)
+ const displayGroupRatio = getDisplayGroupRatio(model, selectedGroup)
- let priceInUSD = (model.model_price || 0) * minRatio
+ let priceInUSD = (model.model_price || 0) * displayGroupRatio
priceInUSD = applyRechargeRate(
priceInUSD,