mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
修复模型广场图片模型价格展示与实收口径不一致
图片计费模型的广场展示价按实收口径计算:档位单价取 分组图片价 > 渠道档位价 > 渠道默认按次价;分组开启生图 独立倍率时实付倍率取独立倍率,不取分组/专属倍率。
This commit is contained in:
@@ -40,6 +40,9 @@ export interface ModelPlazaGroup {
|
||||
peak_end: string
|
||||
peak_rate_multiplier: number
|
||||
is_exclusive: boolean
|
||||
/** 生图独立倍率:true 时图片计费模型的实付倍率取 image_rate_multiplier,不取分组/专属倍率。 */
|
||||
image_rate_independent: boolean
|
||||
image_rate_multiplier: number
|
||||
models: PlazaModel[]
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
:platform="group.platform"
|
||||
:rate-multiplier="group.rate_multiplier"
|
||||
:user-rate-multiplier="group.user_rate_multiplier ?? null"
|
||||
:image-rate-independent="group.image_rate_independent"
|
||||
:image-rate-multiplier="group.image_rate_multiplier"
|
||||
/>
|
||||
<p v-else class="px-5 py-4 text-center text-sm text-gray-400 dark:text-dark-500">
|
||||
{{ t('modelPlaza.detail.noModels') }}
|
||||
|
||||
@@ -134,13 +134,13 @@
|
||||
class="inline-flex items-center gap-1 rounded-md bg-gray-100 px-2 py-0.5 font-mono text-xs text-gray-800 dark:bg-dark-700/60 dark:text-gray-200"
|
||||
>
|
||||
<span class="font-sans text-gray-400 dark:text-dark-500">{{ tierLabel(iv) }}</span>
|
||||
{{ paidRequestPrice(iv.per_request_price)
|
||||
{{ paidRequestPrice(m, iv.per_request_price)
|
||||
}}<span class="font-sans text-gray-400 dark:text-dark-500">{{ perUnitSuffix(m) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<template v-else-if="m.pricing?.per_request_price != null">
|
||||
<span class="font-mono font-semibold text-gray-900 dark:text-gray-50">
|
||||
{{ paidRequestPrice(m.pricing.per_request_price) }}
|
||||
{{ paidRequestPrice(m, m.pricing.per_request_price) }}
|
||||
</span>
|
||||
<span class="ml-1 text-xs text-gray-400 dark:text-dark-500">{{ perUnitSuffix(m) }}</span>
|
||||
</template>
|
||||
@@ -178,11 +178,16 @@
|
||||
<span v-else class="text-gray-400 dark:text-dark-500">-</span>
|
||||
</td>
|
||||
|
||||
<!-- 折扣倍率(专属倍率划线展示原倍率) -->
|
||||
<!-- 折扣倍率(生图独立倍率行展示独立倍率;专属倍率划线展示原倍率) -->
|
||||
<td
|
||||
class="border-l border-gray-100 py-2.5 pl-3 pr-5 text-right align-middle font-mono text-xs dark:border-dark-700/60"
|
||||
>
|
||||
<template v-if="hasCustomRate">
|
||||
<span
|
||||
v-if="usesIndependentImageRate(m)"
|
||||
class="font-bold text-gray-700 dark:text-gray-300"
|
||||
>{{ requestRate(m) }}x</span
|
||||
>
|
||||
<template v-else-if="hasCustomRate">
|
||||
<span class="mr-1 text-gray-400 line-through dark:text-dark-500">{{ rateMultiplier }}x</span>
|
||||
<span class="font-bold text-primary-600 dark:text-primary-400">{{ effectiveRate }}x</span>
|
||||
</template>
|
||||
@@ -215,6 +220,9 @@ const props = defineProps<{
|
||||
rateMultiplier: number
|
||||
/** 用户专属倍率;与默认不同,实付价按此计算并划线展示原倍率。 */
|
||||
userRateMultiplier?: number | null
|
||||
/** 生图独立倍率:true 时图片计费模型的实付倍率取 imageRateMultiplier,不取分组/专属倍率。 */
|
||||
imageRateIndependent?: boolean
|
||||
imageRateMultiplier?: number | null
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -268,10 +276,20 @@ function paidPerMillion(value: number | null | undefined): string {
|
||||
return formatScaled(value * effectiveRate.value, PER_MILLION, MIN_DECIMALS)
|
||||
}
|
||||
|
||||
/** 按次 / 按图片单价(乘生效倍率,不换算 1M)。 */
|
||||
function paidRequestPrice(value: number | null | undefined): string {
|
||||
/** 图片计费模型且分组开启生图独立倍率:实付倍率取独立倍率,与计费口径一致。 */
|
||||
function usesIndependentImageRate(m: PlazaModel): boolean {
|
||||
return billingMode(m) === BILLING_MODE_IMAGE && props.imageRateIndependent === true
|
||||
}
|
||||
|
||||
/** 按次/按图片行的生效倍率。 */
|
||||
function requestRate(m: PlazaModel): number {
|
||||
return usesIndependentImageRate(m) ? (props.imageRateMultiplier ?? 1) : effectiveRate.value
|
||||
}
|
||||
|
||||
/** 按次 / 按图片单价(乘该行生效倍率,不换算 1M)。 */
|
||||
function paidRequestPrice(m: PlazaModel, value: number | null | undefined): string {
|
||||
if (value == null) return '-'
|
||||
return formatScaled(value * effectiveRate.value, 1, MIN_DECIMALS)
|
||||
return formatScaled(value * requestRate(m), 1, MIN_DECIMALS)
|
||||
}
|
||||
|
||||
/** 官方参考价不乘倍率。 */
|
||||
|
||||
@@ -39,9 +39,14 @@ function tokenModel(overrides: Partial<PlazaModel> = {}): PlazaModel {
|
||||
}
|
||||
}
|
||||
|
||||
function mountTable(models: PlazaModel[], rateMultiplier: number, userRateMultiplier?: number | null) {
|
||||
function mountTable(
|
||||
models: PlazaModel[],
|
||||
rateMultiplier: number,
|
||||
userRateMultiplier?: number | null,
|
||||
extraProps?: { imageRateIndependent?: boolean; imageRateMultiplier?: number | null }
|
||||
) {
|
||||
return mount(PlazaModelPricingTable, {
|
||||
props: { models, rateMultiplier, userRateMultiplier: userRateMultiplier ?? null }
|
||||
props: { models, rateMultiplier, userRateMultiplier: userRateMultiplier ?? null, ...extraProps }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -265,6 +270,69 @@ describe('PlazaModelPricingTable', () => {
|
||||
expect(text).toContain('$15.00')
|
||||
})
|
||||
|
||||
it('生图独立倍率开启时,按图价格 × 独立倍率,不乘分组倍率;倍率列展示独立倍率', () => {
|
||||
const model = tokenModel({
|
||||
name: 'gpt-image-2',
|
||||
pricing: {
|
||||
billing_mode: 'image',
|
||||
input_price: null,
|
||||
output_price: null,
|
||||
cache_write_price: null,
|
||||
cache_read_price: null,
|
||||
image_input_price: null,
|
||||
image_output_price: null,
|
||||
per_request_price: null,
|
||||
intervals: [
|
||||
{
|
||||
min_tokens: 0,
|
||||
max_tokens: null,
|
||||
tier_label: '1K',
|
||||
input_price: null,
|
||||
output_price: null,
|
||||
cache_write_price: null,
|
||||
cache_read_price: null,
|
||||
per_request_price: 0.02
|
||||
}
|
||||
]
|
||||
},
|
||||
official_pricing: null
|
||||
})
|
||||
const wrapper = mountTable([model], 0.1, null, {
|
||||
imageRateIndependent: true,
|
||||
imageRateMultiplier: 1
|
||||
})
|
||||
const text = wrapper.text()
|
||||
// 0.02 × 1(独立倍率),而非 0.02 × 0.1
|
||||
expect(text).toContain('$0.02')
|
||||
expect(text).not.toContain('$0.002')
|
||||
// 倍率列展示独立倍率 1x,而非分组倍率 0.1x
|
||||
const rateCell = wrapper.findAll('tbody tr td').at(-1)!
|
||||
expect(rateCell.text()).toBe('1x')
|
||||
})
|
||||
|
||||
it('生图独立倍率关闭时,按图价格仍乘分组/专属生效倍率', () => {
|
||||
const model = tokenModel({
|
||||
name: 'gpt-image-2',
|
||||
pricing: {
|
||||
billing_mode: 'image',
|
||||
input_price: null,
|
||||
output_price: null,
|
||||
cache_write_price: null,
|
||||
cache_read_price: null,
|
||||
image_input_price: null,
|
||||
image_output_price: null,
|
||||
per_request_price: 0.2,
|
||||
intervals: []
|
||||
},
|
||||
official_pricing: null
|
||||
})
|
||||
const wrapper = mountTable([model], 0.1, null, { imageRateIndependent: false })
|
||||
const text = wrapper.text()
|
||||
expect(text).toContain('$0.02')
|
||||
const rateCell = wrapper.findAll('tbody tr td').at(-1)!
|
||||
expect(rateCell.text()).toBe('0.1x')
|
||||
})
|
||||
|
||||
it('按图模型主行展示阶梯芯片,不把 image_output_price(每 token)当按次价', () => {
|
||||
const model = tokenModel({
|
||||
name: 'gpt-image-2',
|
||||
|
||||
Reference in New Issue
Block a user