refactor: 提取正则工具模块并清理废弃代码

前端:
- 新增 model-mapping-regex.ts 工具模块,统一正则验证和 LRU 缓存
- ModelMappingsTab/RoutingTab 重构为使用 computed 缓存匹配结果
- 移除多个组件中未使用的变量和函数
- 添加 HTMLImageElement/HTMLIFrameElement 到 ESLint 全局类型
- 修复 vitest 需要 --experimental-require-module 的问题

后端:
- 移除废弃的异步数据库支持 (get_async_db, AsyncSession)
- 将 async_utils 从 database/ 迁移到 utils/
- 改进 database.py 类型标注
- 修复 email 模块的 aiosmtplib 可选导入类型问题
This commit is contained in:
fawney19
2026-01-15 17:03:19 +08:00
parent bc16c0eec8
commit a223819dd7
25 changed files with 526 additions and 688 deletions
@@ -607,6 +607,7 @@ import { API_FORMAT_ORDER } from '@/api/endpoints/types'
import { recoverKeyHealth } from '@/api/endpoints/health'
import { useToast } from '@/composables/useToast'
import { useCountdownTimer, getProbeCountdown } from '@/composables/useCountdownTimer'
import { MAX_MODEL_NAME_LENGTH, createLRURegexCache, getCompiledModelMappingRegex } from '@/features/models/utils/model-mapping-regex'
const props = defineProps<{
globalModelId: string
@@ -626,6 +627,9 @@ const { tick: countdownTick, start: startCountdownTimer } = useCountdownTimer()
const loading = ref(false)
const error = ref<string | null>(null)
const routingData = ref<ModelRoutingPreviewResponse | null>(null)
const modelMappingRegexCache = createLRURegexCache(200)
const keyMatchedModelsCache = new Map<string, string[]>()
const compiledGlobalModelMappingRegexes = ref<RegExp[]>([])
// 是否为全局 Key 优先模式
const isGlobalKeyMode = computed(() => routingData.value?.priority_mode === 'global_key')
@@ -804,11 +808,24 @@ function toggleProviderInFormat(format: string, providerId: string, endpointId?:
async function loadRoutingData() {
if (!props.globalModelId) return
modelMappingRegexCache.clear()
keyMatchedModelsCache.clear()
compiledGlobalModelMappingRegexes.value = []
loading.value = true
error.value = null
try {
routingData.value = await getGlobalModelRoutingPreview(props.globalModelId)
const data = await getGlobalModelRoutingPreview(props.globalModelId)
const compiled: RegExp[] = []
for (const pattern of data.global_model_mappings || []) {
const regex = getCompiledModelMappingRegex(pattern, modelMappingRegexCache)
if (regex) compiled.push(regex)
}
routingData.value = data
compiledGlobalModelMappingRegexes.value = compiled
} catch (err: any) {
error.value = err.response?.data?.detail || '加载失败'
} finally {
@@ -843,12 +860,24 @@ function hasModelMapping(provider: RoutingProviderInfo): boolean {
// 获取 Key 的 allowed_models 中匹配当前 GlobalModel 的所有模型名
// 逻辑:用 GlobalModel 的 model_mappings(正则模式)去匹配 Key 的 allowed_models 中的值
function getKeyMatchedModels(key: RoutingKeyInfo): string[] {
const cached = keyMatchedModelsCache.get(key.id)
if (cached !== undefined) {
return cached
}
if (!key.allowed_models || key.allowed_models.length === 0) {
keyMatchedModelsCache.set(key.id, [])
return []
}
const globalModelName = routingData.value?.global_model_name
const globalModelMappings = routingData.value?.global_model_mappings || []
if (!globalModelName) {
keyMatchedModelsCache.set(key.id, [])
return []
}
const patterns = compiledGlobalModelMappingRegexes.value
if (patterns.length === 0) {
keyMatchedModelsCache.set(key.id, [])
return []
}
@@ -859,18 +888,17 @@ function getKeyMatchedModels(key: RoutingKeyInfo): string[] {
if (allowedModel === globalModelName) {
continue
}
if (allowedModel.length > MAX_MODEL_NAME_LENGTH) continue
// 用 GlobalModel 的映射模式匹配白名单中的模型名
for (const pattern of globalModelMappings) {
try {
if (new RegExp(`^${pattern}$`, 'i').test(allowedModel)) {
matched.push(allowedModel)
break // 该 allowedModel 已匹配,不需要继续检查其他 pattern
}
} catch {
// 正则无效,跳过
for (const regex of patterns) {
if (regex.test(allowedModel)) {
matched.push(allowedModel)
break // 该 allowedModel 已匹配,不需要继续检查其他 pattern
}
}
}
keyMatchedModelsCache.set(key.id, matched)
return matched
}