refactor: 将 global_priority 和 rate_multiplier 改为按 API 格式配置

- 新增 global_priority_by_format 字段,支持按 API 格式设置全局优先级
- 移除已废弃的 rate_multiplier 字段,统一使用 rate_multipliers
- 移除已废弃的 timeout 字段(providers 和 provider_endpoints 表)
- 更新调度器以支持按格式的优先级排序
- 同步更新前后端类型定义和 API 接口
- 添加数据库迁移脚本,自动迁移现有数据
This commit is contained in:
fawney19
2026-01-16 17:53:27 +08:00
parent 6c98816f9f
commit d5d74339dd
18 changed files with 247 additions and 136 deletions

View File

@@ -307,7 +307,7 @@
</div>
<!-- 速率倍数 -->
<div class="text-sm font-medium tabular-nums text-primary min-w-[40px] text-right">
{{ key.rate_multiplier }}x
{{ key.rate_multipliers?.[format] ?? 1 }}x
</div>
</div>
</div>
@@ -422,9 +422,10 @@ interface KeyWithMeta {
name: string
api_key_masked: string
internal_priority: number
global_priority: number | null
global_priority_by_format: Record<string, number> | null
format_priority: number | null // 当前格式的优先级(后端计算)
priority: number // 用于编辑的优先级
rate_multiplier: number
rate_multipliers: Record<string, number> | null
is_active: boolean
circuit_breaker_open: boolean
provider_name: string
@@ -530,13 +531,25 @@ async function loadKeysByFormat() {
const { default: client } = await import('@/api/client')
const response = await client.get('/api/admin/endpoints/keys/grouped-by-format')
// 每个 key 添加 priority 字段,基于 global_priority 计算显示优先级
// 每个格式独立管理优先级,使用后端返回的 format_priority
const data: Record<string, KeyWithMeta[]> = {}
for (const [format, keys] of Object.entries(response.data as Record<string, any[]>)) {
data[format] = keys.map((key, index) => ({
// 计算该格式下的默认优先级
let maxPriority = 0
for (const key of keys) {
if (key.format_priority != null) {
maxPriority = Math.max(maxPriority, key.format_priority)
}
}
let nextPriority = maxPriority + 1
data[format] = keys.map((key) => ({
...key,
priority: key.global_priority ?? index + 1
// 使用格式特定优先级,如果没有则分配默认值
priority: key.format_priority ?? nextPriority++
}))
// 按优先级排序
data[format].sort((a, b) => a.priority - b.priority)
}
keysByFormat.value = data
@@ -565,8 +578,9 @@ function finishEditKeyPriority(format: string, key: KeyWithMeta, event: FocusEve
const newPriority = parseInt(input.value, 10)
if (!isNaN(newPriority) && newPriority >= 1) {
// 每个格式独立管理优先级,只更新当前格式
key.priority = newPriority
// 按 priority 重新排序
// 重新排序当前格式
keysByFormat.value[format] = [...keysByFormat.value[format]].sort((a, b) => a.priority - b.priority)
}
@@ -697,12 +711,13 @@ function handleKeyDrop(format: string, dropIndex: number) {
return
}
// 直接交换优先级
// 每个格式独立管理优先级,只交换当前格式内的优先级
draggedItem.priority = targetPriority
targetItem.priority = draggedPriority
// 重新排序
// 重新排序当前格式
keysByFormat.value[format] = [...keys].sort((a, b) => a.priority - b.priority)
draggedKey.value[format] = null
dragOverKey.value[format] = null
}
@@ -732,16 +747,22 @@ async function save() {
updateProvider(provider.id, { provider_priority: provider.provider_priority })
)
const keyUpdates: Promise<any>[] = []
// 收集每个 Key 的按格式优先级(保留原有其他格式的配置)
const keyPriorityByFormatMap = new Map<string, Record<string, number>>()
for (const format of Object.keys(keysByFormat.value)) {
const keys = keysByFormat.value[format]
keys.forEach((key) => {
// 使用用户设置的 priority 值,相同 priority 会做负载均衡
keyUpdates.push(updateProviderKey(key.id, { global_priority: key.priority }))
// 合并原有配置,避免丢失未显示格式的优先级
const existing = keyPriorityByFormatMap.get(key.id) || { ...key.global_priority_by_format }
existing[format] = key.priority
keyPriorityByFormatMap.set(key.id, existing)
})
}
const keyUpdates = Array.from(keyPriorityByFormatMap.entries()).map(([keyId, priorityByFormat]) =>
updateProviderKey(keyId, { global_priority_by_format: priorityByFormat })
)
await Promise.all([...providerUpdates, ...keyUpdates])
await loadKeysByFormat()

View File

@@ -1126,17 +1126,10 @@ function getKeyApiFormats(key: EndpointAPIKey, endpoint?: ProviderEndpointWithKe
// 获取密钥在指定 API 格式下的成本倍率
function getKeyRateMultiplier(key: EndpointAPIKey, format: string): number {
// 优先使用 rate_multipliers 中指定格式的倍率
if (key.rate_multipliers && key.rate_multipliers[format] !== undefined) {
return key.rate_multipliers[format]
}
// 如果 rate_multipliers 存在但该格式未配置,说明用户期望使用默认值 1.0
// 只有当 rate_multipliers 完全不存在时,才回退到 rate_multiplier
if (key.rate_multipliers && Object.keys(key.rate_multipliers).length > 0) {
return 1.0
}
// 回退到默认倍率
return key.rate_multiplier || 1.0
return 1.0
}
// 健康度颜色