From 6f11d19877a1e58884a7947309d1f1f2a84fafff Mon Sep 17 00:00:00 2001 From: yyhhyyyyyy Date: Thu, 21 May 2026 11:10:22 +0800 Subject: [PATCH] fix: normalize model pricing display drift (#4985) --- .../models/model-pricing-sheet.tsx | 17 ++---- .../models/model-ratio-visual-editor.tsx | 7 +-- .../system-settings/models/pricing-format.ts | 61 +++++++++++++++++++ 3 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 web/default/src/features/system-settings/models/pricing-format.ts diff --git a/web/default/src/features/system-settings/models/model-pricing-sheet.tsx b/web/default/src/features/system-settings/models/model-pricing-sheet.tsx index 63562f0f..18a8739c 100644 --- a/web/default/src/features/system-settings/models/model-pricing-sheet.tsx +++ b/web/default/src/features/system-settings/models/model-pricing-sheet.tsx @@ -65,6 +65,7 @@ import { import { Switch } from '@/components/ui/switch' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { combineBillingExpr } from '@/features/pricing/lib/billing-expr' +import { formatPricingNumber } from './pricing-format' import { TieredPricingEditor } from './tiered-pricing-editor' const createModelPricingSchema = (t: (key: string) => string) => @@ -216,16 +217,10 @@ function toNumberOrNull(value: unknown): number | null { return Number.isFinite(num) ? num : null } -function formatNumber(value: unknown): string { - const num = toNumberOrNull(value) - if (num === null) return '' - return Number.parseFloat(num.toFixed(12)).toString() -} - function ratioToBasePrice(ratio: unknown): string { const num = toNumberOrNull(ratio) if (num === null) return '' - return formatNumber(num * 2) + return formatPricingNumber(num * 2) } function deriveLanePrice( @@ -236,7 +231,7 @@ function deriveLanePrice( const ratioNumber = toNumberOrNull(ratio) const denominatorNumber = toNumberOrNull(denominator) if (ratioNumber === null || denominatorNumber === null) return fallback - return formatNumber(ratioNumber * denominatorNumber) + return formatPricingNumber(ratioNumber * denominatorNumber) } function createInitialLaneState(data?: ModelRatioData | null) { @@ -513,12 +508,12 @@ export function ModelPricingEditorPanel({ if (lane === 'audioOutput') { const audioInputPrice = toNumberOrNull(nextLanePrices.audioInput) if (audioInputPrice === null || audioInputPrice === 0) return '' - return formatNumber(priceNumber / audioInputPrice) + return formatPricingNumber(priceNumber / audioInputPrice) } const inputPrice = toNumberOrNull(nextPromptPrice) if (inputPrice === null || inputPrice === 0) return '' - return formatNumber(priceNumber / inputPrice) + return formatPricingNumber(priceNumber / inputPrice) } const syncLaneRatios = ( @@ -529,7 +524,7 @@ export function ModelPricingEditorPanel({ const inputPrice = toNumberOrNull(nextPromptPrice) setFormValue( 'ratio', - inputPrice !== null ? formatNumber(inputPrice / 2) : '' + inputPrice !== null ? formatPricingNumber(inputPrice / 2) : '' ) laneConfigs.forEach(({ key }) => { diff --git a/web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx b/web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx index d2e70bb9..19d11af9 100644 --- a/web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx +++ b/web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx @@ -65,6 +65,7 @@ import { ModelPricingSheet, type ModelRatioData, } from './model-pricing-sheet' +import { formatPricingNumber } from './pricing-format' type ModelRatioVisualEditorProps = { modelPrice: string @@ -106,15 +107,11 @@ const toNumberOrNull = (value?: string) => { return Number.isFinite(num) ? num : null } -const formatPrice = (value: number) => { - return Number.parseFloat(value.toFixed(12)).toString() -} - const ratioToPrice = (ratio?: string, denominator?: string) => { const ratioNumber = toNumberOrNull(ratio) const denominatorNumber = denominator ? toNumberOrNull(denominator) : 2 if (ratioNumber === null || denominatorNumber === null) return '' - return formatPrice(ratioNumber * denominatorNumber) + return formatPricingNumber(ratioNumber * denominatorNumber) } const filterBySelectedValues = ( diff --git a/web/default/src/features/system-settings/models/pricing-format.ts b/web/default/src/features/system-settings/models/pricing-format.ts new file mode 100644 index 00000000..18f4b9f2 --- /dev/null +++ b/web/default/src/features/system-settings/models/pricing-format.ts @@ -0,0 +1,61 @@ +/* +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 +*/ +const DISPLAY_DECIMALS = 12 +const SNAP_DECIMALS = 8 +const SNAP_EPSILON = 1e-12 + +function toNumberOrNull(value: unknown): number | null { + if ( + value === '' || + value === null || + value === undefined || + value === false + ) { + return null + } + + const num = Number(value) + return Number.isFinite(num) ? num : null +} + +function roundToDecimals(value: number, decimals: number): number { + const factor = 10 ** decimals + return Math.round(value * factor) / factor +} + +function snapFloatDrift(value: number): number { + const tolerance = Math.max(SNAP_EPSILON, Math.abs(value) * Number.EPSILON * 8) + + for (let decimals = 0; decimals <= SNAP_DECIMALS; decimals += 1) { + const rounded = roundToDecimals(value, decimals) + if (Math.abs(value - rounded) <= tolerance) { + return rounded + } + } + + return value +} + +export function formatPricingNumber(value: unknown): string { + const num = toNumberOrNull(value) + if (num === null) return '' + + const normalized = snapFloatDrift(num) + return Number.parseFloat(normalized.toFixed(DISPLAY_DECIMALS)).toString() +}