Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
97bbb7c8
authored
Jul 08, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(pricing): enhance dynamic pricing calculations with group selection support
parent
28e0115a
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
164 additions
and
103 deletions
+164
-103
web/default/src/features/pricing/components/model-card-grid.tsx
+2
-0
web/default/src/features/pricing/components/model-card.tsx
+55
-34
web/default/src/features/pricing/components/pricing-columns.tsx
+19
-7
web/default/src/features/pricing/components/pricing-table.tsx
+4
-1
web/default/src/features/pricing/index.tsx
+2
-0
web/default/src/features/pricing/lib/dynamic-price.ts
+6
-14
web/default/src/features/pricing/lib/model-helpers.ts
+57
-2
web/default/src/features/pricing/lib/price.ts
+19
-45
No files found.
web/default/src/features/pricing/components/model-card-grid.tsx
View file @
97bbb7c8
...
@@ -36,6 +36,7 @@ export interface ModelCardGridProps {
...
@@ -36,6 +36,7 @@ export interface ModelCardGridProps {
usdExchangeRate
?:
number
usdExchangeRate
?:
number
tokenUnit
?:
TokenUnit
tokenUnit
?:
TokenUnit
showRechargePrice
?:
boolean
showRechargePrice
?:
boolean
selectedGroup
?:
string
}
}
export
function
ModelCardGrid
(
props
:
ModelCardGridProps
)
{
export
function
ModelCardGrid
(
props
:
ModelCardGridProps
)
{
...
@@ -81,6 +82,7 @@ export function ModelCardGrid(props: ModelCardGridProps) {
...
@@ -81,6 +82,7 @@ export function ModelCardGrid(props: ModelCardGridProps) {
priceRate=
{
props
.
priceRate
}
priceRate=
{
props
.
priceRate
}
usdExchangeRate=
{
props
.
usdExchangeRate
}
usdExchangeRate=
{
props
.
usdExchangeRate
}
showRechargePrice=
{
props
.
showRechargePrice
}
showRechargePrice=
{
props
.
showRechargePrice
}
selectedGroup=
{
props
.
selectedGroup
}
perf=
{
perfMap
.
get
(
model
.
model_name
||
''
)
}
perf=
{
perfMap
.
get
(
model
.
model_name
||
''
)
}
onClick=
{
()
=>
props
.
onModelClick
(
model
.
model_name
||
''
)
}
onClick=
{
()
=>
props
.
onModelClick
(
model
.
model_name
||
''
)
}
/>
/>
...
...
web/default/src/features/pricing/components/model-card.tsx
View file @
97bbb7c8
...
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
...
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
For commercial licensing, please contact support@quantumnous.com
*/
*/
import
{
ChevronRight
,
Copy
}
from
'lucide-react'
import
{
ChevronRight
,
Copy
}
from
'lucide-react'
import
{
memo
}
from
'react'
import
{
memo
,
type
ReactNode
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
StatusBadge
}
from
'@/components/status-badge'
import
{
StatusBadge
}
from
'@/components/status-badge'
...
@@ -43,6 +43,7 @@ export interface ModelCardProps {
...
@@ -43,6 +43,7 @@ export interface ModelCardProps {
usdExchangeRate
?:
number
usdExchangeRate
?:
number
tokenUnit
?:
TokenUnit
tokenUnit
?:
TokenUnit
showRechargePrice
?:
boolean
showRechargePrice
?:
boolean
selectedGroup
?:
string
perf
?:
ModelPerfBadgeData
perf
?:
ModelPerfBadgeData
}
}
...
@@ -71,7 +72,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -71,7 +72,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
,
usdExchangeRate
,
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
props
.
model
),
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
props
.
model
,
props
.
selectedGroup
),
})
})
:
null
:
null
...
@@ -87,30 +91,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -87,30 +91,10 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
copyToClipboard
(
props
.
model
.
model_name
||
''
)
copyToClipboard
(
props
.
model
.
model_name
||
''
)
}
}
return
(
let
priceSummary
:
ReactNode
<
div
if
(
dynamicSummary
)
{
className=
{
cn
(
if
(
dynamicSummary
.
isSpecialExpression
)
{
'group relative flex flex-col rounded-xl border p-3 transition-colors sm:p-5'
,
priceSummary
=
(
'hover:bg-muted/20'
)
}
>
{
/* Header: icon + name + price + actions */
}
<
div
className=
'flex items-start justify-between gap-2.5 sm:gap-3'
>
<
div
className=
'flex min-w-0 items-start gap-2.5 sm:gap-3'
>
<
div
className=
'bg-muted/40 flex size-9 shrink-0 items-center justify-center rounded-lg sm:size-10 sm:rounded-xl'
>
{
modelIcon
||
(
<
span
className=
'text-muted-foreground text-sm font-bold'
>
{
initial
}
</
span
>
)
}
</
div
>
<
div
className=
'min-w-0'
>
<
h3
className=
'text-foreground truncate font-mono text-[15px] leading-tight font-bold'
>
{
props
.
model
.
model_name
}
</
h3
>
<
div
className=
'mt-0.5 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-xs sm:mt-1 sm:gap-x-3'
>
{
dynamicSummary
?
(
dynamicSummary
.
isSpecialExpression
?
(
<
span
className=
'min-w-0'
>
<
span
className=
'min-w-0'
>
<
span
className=
'text-amber-700 dark:text-amber-300'
>
<
span
className=
'text-amber-700 dark:text-amber-300'
>
{
t
(
'Special billing expression'
)
}
{
t
(
'Special billing expression'
)
}
...
@@ -119,7 +103,9 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -119,7 +103,9 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
{
dynamicSummary
.
rawExpression
}
{
dynamicSummary
.
rawExpression
}
</
code
>
</
code
>
</
span
>
</
span
>
)
:
dynamicSummary
.
primaryEntries
.
length
>
0
?
(
)
}
else
if
(
dynamicSummary
.
primaryEntries
.
length
>
0
)
{
priceSummary
=
(
<>
<>
{
dynamicSummary
.
primaryEntries
.
map
((
entry
)
=>
(
{
dynamicSummary
.
primaryEntries
.
map
((
entry
)
=>
(
<
span
<
span
...
@@ -134,12 +120,16 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -134,12 +120,16 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
</
span
>
</
span
>
))
}
))
}
</>
</>
)
:
(
)
}
else
{
priceSummary
=
(
<
span
className=
'text-muted-foreground text-xs'
>
<
span
className=
'text-muted-foreground text-xs'
>
{
t
(
'Dynamic Pricing'
)
}
{
t
(
'Dynamic Pricing'
)
}
</
span
>
</
span
>
)
)
)
:
isTokenBased
?
(
}
}
else
if
(
isTokenBased
)
{
priceSummary
=
(
<>
<>
<
span
className=
'text-muted-foreground whitespace-nowrap'
>
<
span
className=
'text-muted-foreground whitespace-nowrap'
>
{
t
(
'Input'
)
}{
' '
}
{
t
(
'Input'
)
}{
' '
}
...
@@ -150,7 +140,8 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -150,7 +140,8 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
props
.
selectedGroup
)
}
)
}
</
span
>
</
span
>
/
{
tokenUnitLabel
}
/
{
tokenUnitLabel
}
...
@@ -164,7 +155,8 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -164,7 +155,8 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
props
.
selectedGroup
)
}
)
}
</
span
>
</
span
>
/
{
tokenUnitLabel
}
/
{
tokenUnitLabel
}
...
@@ -179,25 +171,54 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
...
@@ -179,25 +171,54 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
props
.
selectedGroup
)
}
)
}
</
span
>
</
span
>
</
span
>
</
span
>
)
}
)
}
</>
</>
)
:
(
)
}
else
{
priceSummary
=
(
<
span
className=
'text-muted-foreground whitespace-nowrap'
>
<
span
className=
'text-muted-foreground whitespace-nowrap'
>
<
span
className=
'text-foreground font-mono font-semibold'
>
<
span
className=
'text-foreground font-mono font-semibold'
>
{
formatRequestPrice
(
{
formatRequestPrice
(
props
.
model
,
props
.
model
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
props
.
selectedGroup
)
}
)
}
</
span
>
{
' '
}
</
span
>
{
' '
}
/
{
t
(
'request'
)
}
/
{
t
(
'request'
)
}
</
span
>
</
span
>
)
}
return
(
<
div
className=
{
cn
(
'group relative flex flex-col rounded-xl border p-3 transition-colors sm:p-5'
,
'hover:bg-muted/20'
)
}
)
}
>
{
/* Header: icon + name + price + actions */
}
<
div
className=
'flex items-start justify-between gap-2.5 sm:gap-3'
>
<
div
className=
'flex min-w-0 items-start gap-2.5 sm:gap-3'
>
<
div
className=
'bg-muted/40 flex size-9 shrink-0 items-center justify-center rounded-lg sm:size-10 sm:rounded-xl'
>
{
modelIcon
||
(
<
span
className=
'text-muted-foreground text-sm font-bold'
>
{
initial
}
</
span
>
)
}
</
div
>
<
div
className=
'min-w-0'
>
<
h3
className=
'text-foreground truncate font-mono text-[15px] leading-tight font-bold'
>
{
props
.
model
.
model_name
}
</
h3
>
<
div
className=
'mt-0.5 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-xs sm:mt-1 sm:gap-x-3'
>
{
priceSummary
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
web/default/src/features/pricing/components/pricing-columns.tsx
View file @
97bbb7c8
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
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
{
useTranslation
}
from
'react-i18next'
import
{
import
{
...
@@ -51,6 +51,7 @@ export interface PricingColumnsOptions {
...
@@ -51,6 +51,7 @@ export interface PricingColumnsOptions {
priceRate
?:
number
priceRate
?:
number
usdExchangeRate
?:
number
usdExchangeRate
?:
number
showRechargePrice
?:
boolean
showRechargePrice
?:
boolean
selectedGroup
?:
string
}
}
export
function
usePricingColumns
(
export
function
usePricingColumns
(
...
@@ -62,6 +63,7 @@ export function usePricingColumns(
...
@@ -62,6 +63,7 @@ export function usePricingColumns(
priceRate
=
1
,
priceRate
=
1
,
usdExchangeRate
=
1
,
usdExchangeRate
=
1
,
showRechargePrice
=
false
,
showRechargePrice
=
false
,
selectedGroup
,
}
=
options
}
=
options
const
tokenUnitLabel
=
tokenUnit
===
'K'
?
'1K'
:
'1M'
const
tokenUnitLabel
=
tokenUnit
===
'K'
?
'1K'
:
'1M'
...
@@ -124,7 +126,10 @@ export function usePricingColumns(
...
@@ -124,7 +126,10 @@ export function usePricingColumns(
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
,
usdExchangeRate
,
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
model
),
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
model
,
selectedGroup
),
})
})
if
(
dynamicSummary
)
{
if
(
dynamicSummary
)
{
...
@@ -186,7 +191,8 @@ export function usePricingColumns(
...
@@ -186,7 +191,8 @@ export function usePricingColumns(
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
selectedGroup
)
)
)
)
const
outputPrice
=
stripTrailingZeros
(
const
outputPrice
=
stripTrailingZeros
(
...
@@ -196,7 +202,8 @@ export function usePricingColumns(
...
@@ -196,7 +202,8 @@ export function usePricingColumns(
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
selectedGroup
)
)
)
)
...
@@ -219,7 +226,8 @@ export function usePricingColumns(
...
@@ -219,7 +226,8 @@ export function usePricingColumns(
model
,
model
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
selectedGroup
)
)
)
)
...
@@ -247,7 +255,10 @@ export function usePricingColumns(
...
@@ -247,7 +255,10 @@ export function usePricingColumns(
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
,
usdExchangeRate
,
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
model
),
groupRatioMultiplier
:
getDynamicDisplayGroupRatio
(
model
,
selectedGroup
),
})
})
if
(
dynamicSummary
)
{
if
(
dynamicSummary
)
{
...
@@ -291,7 +302,8 @@ export function usePricingColumns(
...
@@ -291,7 +302,8 @@ export function usePricingColumns(
tokenUnit
,
tokenUnit
,
showRechargePrice
,
showRechargePrice
,
priceRate
,
priceRate
,
usdExchangeRate
usdExchangeRate
,
selectedGroup
)
)
)
)
...
...
web/default/src/features/pricing/components/pricing-table.tsx
View file @
97bbb7c8
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
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
{
useState
,
useCallback
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
...
@@ -38,6 +38,7 @@ export interface PricingTableProps {
...
@@ -38,6 +38,7 @@ export interface PricingTableProps {
usdExchangeRate
?:
number
usdExchangeRate
?:
number
tokenUnit
?:
TokenUnit
tokenUnit
?:
TokenUnit
showRechargePrice
?:
boolean
showRechargePrice
?:
boolean
selectedGroup
?:
string
onModelClick
?:
(
modelName
:
string
)
=>
void
onModelClick
?:
(
modelName
:
string
)
=>
void
}
}
...
@@ -50,6 +51,7 @@ export function PricingTable(props: PricingTableProps) {
...
@@ -50,6 +51,7 @@ export function PricingTable(props: PricingTableProps) {
usdExchangeRate
=
1
,
usdExchangeRate
=
1
,
tokenUnit
=
DEFAULT_TOKEN_UNIT
,
tokenUnit
=
DEFAULT_TOKEN_UNIT
,
showRechargePrice
=
false
,
showRechargePrice
=
false
,
selectedGroup
,
onModelClick
,
onModelClick
,
}
=
props
}
=
props
...
@@ -63,6 +65,7 @@ export function PricingTable(props: PricingTableProps) {
...
@@ -63,6 +65,7 @@ export function PricingTable(props: PricingTableProps) {
priceRate
,
priceRate
,
usdExchangeRate
,
usdExchangeRate
,
showRechargePrice
,
showRechargePrice
,
selectedGroup
,
})
})
const
{
table
}
=
useDataTable
({
const
{
table
}
=
useDataTable
({
...
...
web/default/src/features/pricing/index.tsx
View file @
97bbb7c8
...
@@ -130,6 +130,7 @@ export function Pricing() {
...
@@ -130,6 +130,7 @@ export function Pricing() {
usdExchangeRate=
{
usdExchangeRate
}
usdExchangeRate=
{
usdExchangeRate
}
tokenUnit=
{
tokenUnit
}
tokenUnit=
{
tokenUnit
}
showRechargePrice=
{
showRechargePrice
}
showRechargePrice=
{
showRechargePrice
}
selectedGroup=
{
groupFilter
}
/>
/>
)
)
}
}
...
@@ -141,6 +142,7 @@ export function Pricing() {
...
@@ -141,6 +142,7 @@ export function Pricing() {
usdExchangeRate=
{
usdExchangeRate
}
usdExchangeRate=
{
usdExchangeRate
}
tokenUnit=
{
tokenUnit
}
tokenUnit=
{
tokenUnit
}
showRechargePrice=
{
showRechargePrice
}
showRechargePrice=
{
showRechargePrice
}
selectedGroup=
{
groupFilter
}
onModelClick=
{
handleModelClick
}
onModelClick=
{
handleModelClick
}
/>
/>
)
)
...
...
web/default/src/features/pricing/lib/dynamic-price.ts
View file @
97bbb7c8
...
@@ -28,6 +28,7 @@ import {
...
@@ -28,6 +28,7 @@ import {
type
BillingVar
,
type
BillingVar
,
type
ParsedTier
,
type
ParsedTier
,
}
from
'./billing-expr'
}
from
'./billing-expr'
import
{
getDisplayGroupRatio
}
from
'./model-helpers'
type
DynamicPriceOptions
=
{
type
DynamicPriceOptions
=
{
tokenUnit
:
TokenUnit
tokenUnit
:
TokenUnit
...
@@ -65,20 +66,11 @@ export function isDynamicPricingModel(model: PricingModel): boolean {
...
@@ -65,20 +66,11 @@ export function isDynamicPricingModel(model: PricingModel): boolean {
return
model
.
billing_mode
===
'tiered_expr'
&&
Boolean
(
model
.
billing_expr
)
return
model
.
billing_mode
===
'tiered_expr'
&&
Boolean
(
model
.
billing_expr
)
}
}
export
function
getDynamicDisplayGroupRatio
(
model
:
PricingModel
):
number
{
export
function
getDynamicDisplayGroupRatio
(
const
groups
=
Array
.
isArray
(
model
.
enable_groups
)
?
model
.
enable_groups
:
[]
model
:
PricingModel
,
const
ratios
=
model
.
group_ratio
||
{}
selectedGroup
?:
string
if
(
groups
.
length
===
0
)
return
1
):
number
{
return
getDisplayGroupRatio
(
model
,
selectedGroup
)
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
}
}
function
applyRechargeRate
(
function
applyRechargeRate
(
...
...
web/default/src/features/pricing/lib/model-helpers.ts
View file @
97bbb7c8
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
...
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
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'
import
type
{
PricingModel
}
from
'../types'
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
...
@@ -40,10 +40,65 @@ export function getAvailableGroups(
...
@@ -40,10 +40,65 @@ export function getAvailableGroups(
}
}
/**
/**
* Read a configured group ratio while preserving valid zero ratios.
*/
export
function
getConfiguredGroupRatio
(
groupRatio
:
Record
<
string
,
number
>
,
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
* Replace model placeholder in endpoint path
*/
*/
export
function
replaceModelInPath
(
path
:
string
,
modelName
:
string
):
string
{
export
function
replaceModelInPath
(
path
:
string
,
modelName
:
string
):
string
{
return
path
.
replace
(
/
\{
model
\}
/g
,
modelName
)
return
path
.
replace
All
(
'{model}'
,
modelName
)
}
}
/**
/**
...
...
web/default/src/features/pricing/lib/price.ts
View file @
97bbb7c8
...
@@ -20,6 +20,7 @@ import { formatCurrencyFromUSD } from '@/lib/currency'
...
@@ -20,6 +20,7 @@ import { formatCurrencyFromUSD } from '@/lib/currency'
import
{
QUOTA_TYPE_VALUES
,
TOKEN_UNIT_DIVISORS
}
from
'../constants'
import
{
QUOTA_TYPE_VALUES
,
TOKEN_UNIT_DIVISORS
}
from
'../constants'
import
type
{
PricingModel
,
TokenUnit
,
PriceType
}
from
'../types'
import
type
{
PricingModel
,
TokenUnit
,
PriceType
}
from
'../types'
import
{
getConfiguredGroupRatio
,
getDisplayGroupRatio
}
from
'./model-helpers'
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Price Calculation Utilities
// Price Calculation Utilities
...
@@ -36,11 +37,11 @@ export function stripTrailingZeros(formatted: string): string {
...
@@ -36,11 +37,11 @@ export function stripTrailingZeros(formatted: string): string {
const
[,
symbol
,
number
,
suffix
]
=
match
const
[,
symbol
,
number
,
suffix
]
=
match
// Remove commas for processing
// Remove commas for processing
const
cleanNumber
=
number
.
replace
(
/,/g
,
''
)
const
cleanNumber
=
number
.
replace
All
(
','
,
''
)
// Convert to number and back to remove trailing zeros
// Convert to number and back to remove trailing zeros
const
parsed
=
parseFloat
(
cleanNumber
)
const
parsed
=
Number
.
parseFloat
(
cleanNumber
)
if
(
isNaN
(
parsed
))
return
formatted
if
(
Number
.
isNaN
(
parsed
))
return
formatted
// Convert to string, which automatically removes trailing zeros
// Convert to string, which automatically removes trailing zeros
let
result
=
parsed
.
toString
()
let
result
=
parsed
.
toString
()
...
@@ -54,27 +55,6 @@ export function stripTrailingZeros(formatted: string): string {
...
@@ -54,27 +55,6 @@ export function stripTrailingZeros(formatted: string): string {
}
}
/**
/**
* Find minimum group ratio from enabled groups
*/
function
getMinGroupRatio
(
enableGroups
:
string
[],
groupRatio
:
Record
<
string
,
number
>
):
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.
* Calculate token price in USD.
*
*
* Returns NaN when the required ratio field is missing/null so callers can
* Returns NaN when the required ratio field is missing/null so callers can
...
@@ -95,26 +75,26 @@ function calculateTokenPrice(
...
@@ -95,26 +75,26 @@ function calculateTokenPrice(
case
'cache'
:
case
'cache'
:
return
hasRatio
(
model
.
cache_ratio
)
return
hasRatio
(
model
.
cache_ratio
)
?
base
*
Number
(
model
.
cache_ratio
)
?
base
*
Number
(
model
.
cache_ratio
)
:
NaN
:
N
umber
.
N
aN
case
'create_cache'
:
case
'create_cache'
:
return
hasRatio
(
model
.
create_cache_ratio
)
return
hasRatio
(
model
.
create_cache_ratio
)
?
base
*
Number
(
model
.
create_cache_ratio
)
?
base
*
Number
(
model
.
create_cache_ratio
)
:
NaN
:
N
umber
.
N
aN
case
'image'
:
case
'image'
:
return
hasRatio
(
model
.
image_ratio
)
return
hasRatio
(
model
.
image_ratio
)
?
base
*
Number
(
model
.
image_ratio
)
?
base
*
Number
(
model
.
image_ratio
)
:
NaN
:
N
umber
.
N
aN
case
'audio_input'
:
case
'audio_input'
:
return
hasRatio
(
model
.
audio_ratio
)
return
hasRatio
(
model
.
audio_ratio
)
?
base
*
Number
(
model
.
audio_ratio
)
?
base
*
Number
(
model
.
audio_ratio
)
:
NaN
:
N
umber
.
N
aN
case
'audio_output'
:
case
'audio_output'
:
return
hasRatio
(
model
.
audio_ratio
)
&&
return
hasRatio
(
model
.
audio_ratio
)
&&
hasRatio
(
model
.
audio_completion_ratio
)
hasRatio
(
model
.
audio_completion_ratio
)
?
base
*
?
base
*
Number
(
model
.
audio_ratio
)
*
Number
(
model
.
audio_ratio
)
*
Number
(
model
.
audio_completion_ratio
)
Number
(
model
.
audio_completion_ratio
)
:
NaN
:
N
umber
.
N
aN
}
}
}
}
...
@@ -167,19 +147,16 @@ export function formatPrice(
...
@@ -167,19 +147,16 @@ export function formatPrice(
tokenUnit
:
TokenUnit
,
tokenUnit
:
TokenUnit
,
showWithRecharge
=
false
,
showWithRecharge
=
false
,
priceRate
=
1
,
priceRate
=
1
,
usdExchangeRate
=
1
usdExchangeRate
=
1
,
selectedGroup
?:
string
):
string
{
):
string
{
if
(
model
.
quota_type
===
QUOTA_TYPE_VALUES
.
REQUEST
)
{
if
(
model
.
quota_type
===
QUOTA_TYPE_VALUES
.
REQUEST
)
{
return
'-'
return
'-'
}
}
const
enableGroups
=
Array
.
isArray
(
model
.
enable_groups
)
const
displayGroupRatio
=
getDisplayGroupRatio
(
model
,
selectedGroup
)
?
model
.
enable_groups
:
[]
const
groupRatio
=
model
.
group_ratio
||
{}
const
minRatio
=
getMinGroupRatio
(
enableGroups
,
groupRatio
)
let
priceInUSD
=
calculateTokenPrice
(
model
,
type
,
min
Ratio
)
let
priceInUSD
=
calculateTokenPrice
(
model
,
type
,
displayGroup
Ratio
)
priceInUSD
=
applyRechargeRate
(
priceInUSD
=
applyRechargeRate
(
priceInUSD
,
priceInUSD
,
showWithRecharge
,
showWithRecharge
,
...
@@ -212,7 +189,7 @@ export function formatGroupPrice(
...
@@ -212,7 +189,7 @@ export function formatGroupPrice(
return
'-'
return
'-'
}
}
const
ratio
=
g
roupRatio
[
group
]
||
1
const
ratio
=
g
etConfiguredGroupRatio
(
groupRatio
,
group
)
let
priceInUSD
=
calculateTokenPrice
(
model
,
type
,
ratio
)
let
priceInUSD
=
calculateTokenPrice
(
model
,
type
,
ratio
)
priceInUSD
=
applyRechargeRate
(
priceInUSD
=
applyRechargeRate
(
...
@@ -245,7 +222,7 @@ export function formatFixedPrice(
...
@@ -245,7 +222,7 @@ export function formatFixedPrice(
return
'-'
return
'-'
}
}
const
ratio
=
g
roupRatio
[
group
]
||
1
const
ratio
=
g
etConfiguredGroupRatio
(
groupRatio
,
group
)
let
priceInUSD
=
(
model
.
model_price
||
0
)
*
ratio
let
priceInUSD
=
(
model
.
model_price
||
0
)
*
ratio
priceInUSD
=
applyRechargeRate
(
priceInUSD
=
applyRechargeRate
(
...
@@ -269,19 +246,16 @@ export function formatRequestPrice(
...
@@ -269,19 +246,16 @@ export function formatRequestPrice(
model
:
PricingModel
,
model
:
PricingModel
,
showWithRecharge
=
false
,
showWithRecharge
=
false
,
priceRate
=
1
,
priceRate
=
1
,
usdExchangeRate
=
1
usdExchangeRate
=
1
,
selectedGroup
?:
string
):
string
{
):
string
{
if
(
model
.
quota_type
!==
QUOTA_TYPE_VALUES
.
REQUEST
)
{
if
(
model
.
quota_type
!==
QUOTA_TYPE_VALUES
.
REQUEST
)
{
return
'-'
return
'-'
}
}
const
enableGroups
=
Array
.
isArray
(
model
.
enable_groups
)
const
displayGroupRatio
=
getDisplayGroupRatio
(
model
,
selectedGroup
)
?
model
.
enable_groups
:
[]
const
groupRatio
=
model
.
group_ratio
||
{}
const
minRatio
=
getMinGroupRatio
(
enableGroups
,
groupRatio
)
let
priceInUSD
=
(
model
.
model_price
||
0
)
*
min
Ratio
let
priceInUSD
=
(
model
.
model_price
||
0
)
*
displayGroup
Ratio
priceInUSD
=
applyRechargeRate
(
priceInUSD
=
applyRechargeRate
(
priceInUSD
,
priceInUSD
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment