mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-02 15:52:26 +08:00
fix: TieredPricingEditor 缓存价格处理优化
- 缓存价格计算精度从 2 位小数改为 4 位,支持更精细的价格 - 分离 syncToParent 和 getFinalTiers 职责: - syncToParent: 只同步用户实际输入的值 - getFinalTiers: 提交时获取包含自动计算的最终数据 - GlobalModelFormDialog 和 ProviderModelFormDialog 提交时调用 getFinalTiers
This commit is contained in:
@@ -100,7 +100,7 @@
|
||||
<!-- 价格配置 -->
|
||||
<section class="space-y-3">
|
||||
<h4 class="font-medium text-sm">价格配置</h4>
|
||||
<TieredPricingEditor v-model="tieredPricing" :show-cache1h="form.supported_capabilities?.includes('cache_1h')" />
|
||||
<TieredPricingEditor ref="tieredPricingEditorRef" v-model="tieredPricing" :show-cache1h="form.supported_capabilities?.includes('cache_1h')" />
|
||||
|
||||
<!-- 按次计费 -->
|
||||
<div class="flex items-center gap-3 pt-2 border-t">
|
||||
@@ -161,6 +161,7 @@ const emit = defineEmits<{
|
||||
|
||||
const { success, error: showError } = useToast()
|
||||
const submitting = ref(false)
|
||||
const tieredPricingEditorRef = ref<InstanceType<typeof TieredPricingEditor> | null>(null)
|
||||
|
||||
// 阶梯计费配置(统一使用,固定价格就是单阶梯)
|
||||
const tieredPricing = ref<TieredPricingConfig | null>(null)
|
||||
@@ -275,6 +276,10 @@ async function handleSubmit() {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取包含自动计算缓存价格的最终数据
|
||||
const finalTiers = tieredPricingEditorRef.value?.getFinalTiers()
|
||||
const finalTieredPricing = finalTiers ? { tiers: finalTiers } : tieredPricing.value
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
if (isEditMode.value && props.model) {
|
||||
@@ -283,7 +288,7 @@ async function handleSubmit() {
|
||||
description: form.value.description,
|
||||
// 使用 null 而不是 undefined 来显式清空字段
|
||||
default_price_per_request: form.value.default_price_per_request ?? null,
|
||||
default_tiered_pricing: tieredPricing.value,
|
||||
default_tiered_pricing: finalTieredPricing,
|
||||
default_supports_streaming: form.value.default_supports_streaming,
|
||||
default_supports_image_generation: form.value.default_supports_image_generation,
|
||||
default_supports_vision: form.value.default_supports_vision,
|
||||
@@ -300,7 +305,7 @@ async function handleSubmit() {
|
||||
display_name: form.value.display_name!,
|
||||
description: form.value.description,
|
||||
default_price_per_request: form.value.default_price_per_request || undefined,
|
||||
default_tiered_pricing: tieredPricing.value,
|
||||
default_tiered_pricing: finalTieredPricing,
|
||||
default_supports_streaming: form.value.default_supports_streaming,
|
||||
default_supports_image_generation: form.value.default_supports_image_generation,
|
||||
default_supports_vision: form.value.default_supports_vision,
|
||||
|
||||
@@ -287,32 +287,32 @@ function formatTokens(tokens: number): string {
|
||||
// 缓存价格自动计算
|
||||
function getAutoCacheCreation(index: number): number {
|
||||
const inputPrice = localTiers.value[index]?.input_price_per_1m || 0
|
||||
return parseFloat((inputPrice * 1.25).toFixed(2))
|
||||
return parseFloat((inputPrice * 1.25).toFixed(4))
|
||||
}
|
||||
|
||||
function getAutoCacheRead(index: number): number {
|
||||
const inputPrice = localTiers.value[index]?.input_price_per_1m || 0
|
||||
return parseFloat((inputPrice * 0.1).toFixed(2))
|
||||
return parseFloat((inputPrice * 0.1).toFixed(4))
|
||||
}
|
||||
|
||||
function getAutoCache1h(index: number): number {
|
||||
const inputPrice = localTiers.value[index]?.input_price_per_1m || 0
|
||||
return parseFloat((inputPrice * 2).toFixed(2))
|
||||
return parseFloat((inputPrice * 2).toFixed(4))
|
||||
}
|
||||
|
||||
function getCacheCreationPlaceholder(index: number): string {
|
||||
const auto = getAutoCacheCreation(index)
|
||||
return auto > 0 ? auto.toFixed(2) : '自动'
|
||||
return auto > 0 ? String(auto) : '自动'
|
||||
}
|
||||
|
||||
function getCacheReadPlaceholder(index: number): string {
|
||||
const auto = getAutoCacheRead(index)
|
||||
return auto > 0 ? auto.toFixed(2) : '自动'
|
||||
return auto > 0 ? String(auto) : '自动'
|
||||
}
|
||||
|
||||
function getCache1hPlaceholder(index: number): string {
|
||||
const auto = getAutoCache1h(index)
|
||||
return auto > 0 ? auto.toFixed(2) : '自动'
|
||||
return auto > 0 ? String(auto) : '自动'
|
||||
}
|
||||
|
||||
function getCacheCreationDisplay(index: number): string | number {
|
||||
@@ -346,7 +346,7 @@ function getCache1hDisplay(index: number): string | number {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 同步到父组件(包含自动计算的缓存价格)
|
||||
// 同步到父组件(只同步用户实际输入的值,不自动填充)
|
||||
function syncToParent() {
|
||||
if (validationError.value) return
|
||||
|
||||
@@ -357,6 +357,36 @@ function syncToParent() {
|
||||
output_price_per_1m: t.output_price_per_1m,
|
||||
}
|
||||
|
||||
// 缓存创建价格:只有手动设置才同步
|
||||
if (cacheManuallySet[i]?.creation && t.cache_creation_price_per_1m != null) {
|
||||
tier.cache_creation_price_per_1m = t.cache_creation_price_per_1m
|
||||
}
|
||||
|
||||
// 缓存读取价格:只有手动设置才同步
|
||||
if (cacheManuallySet[i]?.read && t.cache_read_price_per_1m != null) {
|
||||
tier.cache_read_price_per_1m = t.cache_read_price_per_1m
|
||||
}
|
||||
|
||||
// 缓存 TTL 价格(1h 缓存)- 只有启用 1h 缓存能力且手动设置时才处理
|
||||
if (props.showCache1h && cacheManuallySet[i]?.cache1h && t.cache_ttl_pricing?.length) {
|
||||
tier.cache_ttl_pricing = t.cache_ttl_pricing
|
||||
}
|
||||
|
||||
return tier
|
||||
})
|
||||
|
||||
emit('update:modelValue', { tiers })
|
||||
}
|
||||
|
||||
// 获取最终提交的数据(包含自动计算的缓存价格)
|
||||
function getFinalTiers(): PricingTier[] {
|
||||
return localTiers.value.map((t, i) => {
|
||||
const tier: PricingTier = {
|
||||
up_to: t.up_to,
|
||||
input_price_per_1m: t.input_price_per_1m,
|
||||
output_price_per_1m: t.output_price_per_1m,
|
||||
}
|
||||
|
||||
// 缓存创建价格:手动设置则用设置值,否则自动计算
|
||||
if (cacheManuallySet[i]?.creation && t.cache_creation_price_per_1m != null) {
|
||||
tier.cache_creation_price_per_1m = t.cache_creation_price_per_1m
|
||||
@@ -374,20 +404,21 @@ function syncToParent() {
|
||||
// 缓存 TTL 价格(1h 缓存)- 只有启用 1h 缓存能力时才处理
|
||||
if (props.showCache1h) {
|
||||
if (cacheManuallySet[i]?.cache1h && t.cache_ttl_pricing?.length) {
|
||||
// 手动设置则用设置值
|
||||
tier.cache_ttl_pricing = t.cache_ttl_pricing
|
||||
} else if (t.input_price_per_1m > 0) {
|
||||
// 否则自动计算
|
||||
tier.cache_ttl_pricing = [{ ttl_minutes: 60, cache_creation_price_per_1m: getAutoCache1h(i) }]
|
||||
}
|
||||
}
|
||||
|
||||
return tier
|
||||
})
|
||||
|
||||
emit('update:modelValue', { tiers })
|
||||
}
|
||||
|
||||
// 暴露给父组件调用
|
||||
defineExpose({
|
||||
getFinalTiers,
|
||||
})
|
||||
|
||||
function parseFloatInput(value: string | number): number {
|
||||
const num = typeof value === 'string' ? parseFloat(value) : value
|
||||
return isNaN(num) ? 0 : num
|
||||
@@ -515,9 +546,13 @@ function removeTier(index: number) {
|
||||
delete cacheManuallySet[index]
|
||||
|
||||
// 重新整理 cacheManuallySet 的索引
|
||||
const newManuallySet: Record<number, { creation: boolean; read: boolean }> = {}
|
||||
const newManuallySet: Record<
|
||||
number,
|
||||
{ creation: boolean; read: boolean; cache1h: boolean }
|
||||
> = {}
|
||||
localTiers.value.forEach((_, i) => {
|
||||
newManuallySet[i] = cacheManuallySet[i] || { creation: false, read: false }
|
||||
newManuallySet[i] =
|
||||
cacheManuallySet[i] || { creation: false, read: false, cache1h: false }
|
||||
})
|
||||
Object.keys(cacheManuallySet).forEach(k => delete cacheManuallySet[Number(k)])
|
||||
Object.assign(cacheManuallySet, newManuallySet)
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<!-- 价格配置 -->
|
||||
<div class="space-y-4">
|
||||
<h4 class="font-semibold text-sm border-b pb-2">价格配置</h4>
|
||||
<TieredPricingEditor v-model="tieredPricing" :show-cache1h="showCache1h" />
|
||||
<TieredPricingEditor ref="tieredPricingEditorRef" v-model="tieredPricing" :show-cache1h="showCache1h" />
|
||||
|
||||
<!-- 按次计费 -->
|
||||
<div class="flex items-center gap-3 pt-2 border-t">
|
||||
@@ -171,6 +171,8 @@ const emit = defineEmits<{
|
||||
|
||||
const { error: showError, success: showSuccess } = useToast()
|
||||
|
||||
const tieredPricingEditorRef = ref<InstanceType<typeof TieredPricingEditor> | null>(null)
|
||||
|
||||
const isEditing = computed(() => !!props.editingModel)
|
||||
|
||||
// 计算是否显示 1h 缓存输入框
|
||||
@@ -325,11 +327,15 @@ async function handleSubmit() {
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
// 获取包含自动计算缓存价格的最终数据
|
||||
const finalTiers = tieredPricingEditorRef.value?.getFinalTiers()
|
||||
const finalTieredPricing = finalTiers ? { tiers: finalTiers } : tieredPricing.value
|
||||
|
||||
if (isEditing.value && props.editingModel) {
|
||||
// 编辑模式
|
||||
// 注意:使用 null 而不是 undefined 来显式清空字段(undefined 会被 JSON 序列化忽略)
|
||||
await updateModel(props.providerId, props.editingModel.id, {
|
||||
tiered_pricing: tieredPricing.value,
|
||||
tiered_pricing: finalTieredPricing,
|
||||
price_per_request: form.value.price_per_request ?? null,
|
||||
supports_vision: form.value.supports_vision,
|
||||
supports_function_calling: form.value.supports_function_calling,
|
||||
@@ -346,7 +352,7 @@ async function handleSubmit() {
|
||||
global_model_id: form.value.global_model_id,
|
||||
provider_model_name: selectedModel?.name || '',
|
||||
// 只有修改了才提交,否则传 undefined 让后端继承 GlobalModel 配置
|
||||
tiered_pricing: tieredPricingModified.value ? tieredPricing.value : undefined,
|
||||
tiered_pricing: tieredPricingModified.value ? finalTieredPricing : undefined,
|
||||
price_per_request: form.value.price_per_request,
|
||||
supports_vision: form.value.supports_vision,
|
||||
supports_function_calling: form.value.supports_function_calling,
|
||||
|
||||
Reference in New Issue
Block a user