feat: 缓存计费细分、能力匹配优化、用户模型调用计数

1. 缓存创建 tokens 区分 5min/1h TTL,支持按缓存时长差异化计费
   - Usage 表新增 cache_creation_input_tokens_5m/1h 字段
   - Claude handler 解析新格式 (ephemeral_5m/1h, claude_cache_creation_5/1h)
   - 计费规则支持 cache_ttl_pricing 覆盖 cache_creation 价格

2. 能力匹配机制优化
   - COMPATIBLE 能力不再硬过滤,改为排序阶段通过 capability_miss_count 优先级处理
   - cache_1h 改为 COMPATIBLE + REQUEST_PARAM(自动检测请求体中的 ttl=1h)
   - gemini_files 改为 EXCLUSIVE + REQUEST_PARAM(自动检测 fileData.fileUri)
   - 移除前端模型偏好/能力配置 UI(不再需要用户手动配置)

3. 新增用户-模型维度调用次数计数器 (UserModelUsageCount)
   - 原子递增,避免从 Usage 表聚合查询
   - 前端模型目录和用户可用模型列表展示调用次数

4. 其他改进
   - global_model_id 改为必填(NOT NULL),清理孤立模型
   - 模型映射对话框支持从上游获取模型列表并分组折叠
   - 端点测试不再依赖端点启用状态
   - 异步任务页面对普通用户隐藏用户信息列
   - Dashboard 响应式布局断点调整 (sm -> lg)
   - 号池管理仅展示已启用号池的提供商
This commit is contained in:
fawney19
2026-02-28 11:44:08 +08:00
parent 82bbed2720
commit ecb16d345a
59 changed files with 1053 additions and 608 deletions

View File

@@ -139,26 +139,6 @@
</div>
</div>
<!-- 模型偏好 -->
<div
v-if="model.supported_capabilities && model.supported_capabilities.length > 0"
class="space-y-3"
>
<h4 class="font-semibold text-sm">
模型偏好
</h4>
<div class="flex flex-wrap gap-2">
<Badge
v-for="cap in model.supported_capabilities"
:key="cap"
variant="outline"
class="text-xs"
>
{{ getCapabilityDisplayName(cap) }}
</Badge>
</div>
</div>
<!-- 默认定价 -->
<div class="space-y-3">
<h4 class="font-semibold text-sm">
@@ -475,12 +455,10 @@ import { getGlobalModelRoutingPreview } from '@/api/global-models'
// 使用外部类型定义
import type { GlobalModelResponse } from '@/api/global-models'
import type { TieredPricingConfig, PricingTier, ModelRoutingPreviewResponse } from '@/api/endpoints/types'
import type { CapabilityDefinition } from '@/api/endpoints'
import type { RoutingProviderInfo } from '@/api/global-models'
const props = withDefaults(defineProps<Props>(), {
hasBlockingDialogOpen: false,
capabilities: () => [],
})
const emit = defineEmits<{
'update:open': [value: boolean]
@@ -500,7 +478,6 @@ interface Props {
model: GlobalModelResponse | null
open: boolean
hasBlockingDialogOpen?: boolean
capabilities?: CapabilityDefinition[]
}
// RoutingTab 引用
@@ -571,12 +548,6 @@ defineExpose({
refreshRoutingData
})
// 根据能力名称获取显示名称
function getCapabilityDisplayName(capName: string): string {
const cap = props.capabilities?.find(c => c.name === capName)
return cap?.display_name || capName
}
// 检测是否有视频分辨率计费配置
const hasVideoPricing = computed(() => {
const priceByResolution = props.model?.config?.billing?.video?.price_per_second_by_resolution