mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +08:00
feat(billing): add image output pricing and usage tracking
This commit is contained in:
@@ -203,6 +203,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2 border-t border-border/60 pt-3">
|
||||
<Checkbox
|
||||
:model-value="isImageGenerationEnabled"
|
||||
class="mt-0.5"
|
||||
@update:model-value="setImageGenerationEnabled"
|
||||
/>
|
||||
<div class="space-y-1">
|
||||
<div class="text-sm font-medium">
|
||||
图片模型
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
启用图片输出计费,并展开尺寸 × 质量矩阵价格。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -215,6 +230,7 @@
|
||||
ref="tieredPricingEditorRef"
|
||||
v-model="tieredPricing"
|
||||
:show-cache1h="true"
|
||||
:show-image-pricing="isImageGenerationEnabled"
|
||||
/>
|
||||
<div class="flex items-center gap-3 pt-2 border-t">
|
||||
<Label class="text-xs whitespace-nowrap">按次计费</Label>
|
||||
@@ -534,6 +550,14 @@ const isEmbeddingEnabled = computed(() => {
|
||||
|| form.value.config?.model_type === 'embedding'
|
||||
})
|
||||
|
||||
const isImageGenerationEnabled = computed(() => {
|
||||
return form.value.supported_capabilities?.includes('image_generation') === true
|
||||
|| form.value.config?.image_generation === true
|
||||
|| form.value.config?.model_type === 'image'
|
||||
|| (Array.isArray(form.value.config?.api_formats)
|
||||
&& form.value.config.api_formats.some((format) => String(format).endsWith(':image')))
|
||||
})
|
||||
|
||||
const KEEP_FALSE_CONFIG_KEYS = new Set(['streaming'])
|
||||
|
||||
// 设置 config 字段
|
||||
@@ -576,6 +600,20 @@ function setEmbeddingEnabled(enabled: boolean) {
|
||||
form.value.supported_capabilities = [...caps]
|
||||
}
|
||||
|
||||
function setImageGenerationEnabled(value: boolean | 'indeterminate') {
|
||||
const enabled = value === true
|
||||
const caps = new Set(form.value.supported_capabilities || [])
|
||||
if (enabled) {
|
||||
caps.add('image_generation')
|
||||
setConfigField('image_generation', true)
|
||||
} else {
|
||||
caps.delete('image_generation')
|
||||
setConfigField('image_generation', undefined)
|
||||
if (form.value.config?.model_type === 'image') setConfigField('model_type', undefined)
|
||||
}
|
||||
form.value.supported_capabilities = [...caps]
|
||||
}
|
||||
|
||||
function getNested(obj: unknown, path: string): unknown {
|
||||
if (!obj || typeof obj !== 'object') return undefined
|
||||
const parts = path.split('.').filter(Boolean)
|
||||
@@ -753,7 +791,10 @@ function selectModel(model: ModelsDevModelItem) {
|
||||
if (model.inputModalities?.length) config.input_modalities = model.inputModalities
|
||||
if (model.outputModalities?.length) config.output_modalities = model.outputModalities
|
||||
form.value.config = config
|
||||
form.value.supported_capabilities = model.supportsEmbedding ? ['embedding'] : []
|
||||
const supportedCapabilities = new Set<string>()
|
||||
if (model.supportsEmbedding) supportedCapabilities.add('embedding')
|
||||
if (model.outputModalities?.includes('image')) supportedCapabilities.add('image_generation')
|
||||
form.value.supported_capabilities = [...supportedCapabilities]
|
||||
if (model.supportsEmbedding) {
|
||||
setEmbeddingEnabled(true)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,10 @@
|
||||
添加价格阶梯
|
||||
</Button>
|
||||
|
||||
<div class="rounded-lg border bg-muted/10 p-3 space-y-3">
|
||||
<div
|
||||
v-if="showImagePricing"
|
||||
class="rounded-lg border bg-muted/10 p-3 space-y-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-end justify-between gap-3">
|
||||
<Label class="text-xs font-medium">图像输出矩阵 ($/张)</Label>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -211,6 +214,7 @@ const IMAGE_OUTPUT_QUALITIES: ImageOutputQuality[] = ['low', 'medium', 'high']
|
||||
const props = defineProps<{
|
||||
modelValue?: TieredPricingConfig | null
|
||||
showCache1h?: boolean
|
||||
showImagePricing?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -481,6 +485,9 @@ defineExpose({
|
||||
|
||||
function buildPricingConfig(tiers: PricingTier[]): TieredPricingConfig {
|
||||
const config: TieredPricingConfig = { tiers }
|
||||
if (!props.showImagePricing) {
|
||||
return config
|
||||
}
|
||||
const matrix = normalizedImageOutputPrices()
|
||||
if (Object.keys(matrix).length > 0) {
|
||||
config.image_output_prices = matrix
|
||||
|
||||
Reference in New Issue
Block a user