mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix: 修复模型测试时正则映射支持及缓存装饰器实例方法兼容
- ModelsTab 加载时并行获取模型映射预览数据 - 测试模型时自动识别正则映射并使用映射名称和指定 key - 密钥变更后并行刷新模型列表和模型映射 - 修复 cache_decorator 对实例方法的 context 参数解析
This commit is contained in:
@@ -383,6 +383,7 @@
|
||||
<!-- 模型查看 -->
|
||||
<ModelsTab
|
||||
v-if="provider"
|
||||
ref="modelsTabRef"
|
||||
:key="`models-${provider.id}`"
|
||||
:provider="provider"
|
||||
:endpoints="endpoints"
|
||||
@@ -585,6 +586,7 @@ const editingModel = ref<Model | null>(null)
|
||||
const deleteModelConfirmOpen = ref(false)
|
||||
const modelToDelete = ref<Model | null>(null)
|
||||
const batchAssignDialogOpen = ref(false)
|
||||
const modelsTabRef = ref<InstanceType<typeof ModelsTab> | null>(null)
|
||||
const modelMappingTabRef = ref<InstanceType<typeof ModelMappingTab> | null>(null)
|
||||
|
||||
// 密钥列表拖拽排序状态
|
||||
@@ -797,6 +799,11 @@ async function handleRecoverKey(key: EndpointAPIKey) {
|
||||
|
||||
async function handleKeyChanged() {
|
||||
await loadEndpoints()
|
||||
// 并行刷新模型列表和模型映射(因为模型权限会影响正则映射预览)
|
||||
await Promise.all([
|
||||
modelsTabRef.value?.reload(),
|
||||
modelMappingTabRef.value?.reload()
|
||||
])
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,13 @@ import Card from '@/components/ui/card.vue'
|
||||
import Button from '@/components/ui/button.vue'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { useClipboard } from '@/composables/useClipboard'
|
||||
import { getProviderModels, type Model, testModel } from '@/api/endpoints'
|
||||
import {
|
||||
getProviderModels,
|
||||
getProviderMappingPreview,
|
||||
testModel,
|
||||
type Model,
|
||||
type ProviderMappingPreviewResponse
|
||||
} from '@/api/endpoints'
|
||||
import { updateModel } from '@/api/endpoints/models'
|
||||
import { parseTestModelError } from '@/utils/errorParser'
|
||||
|
||||
@@ -239,6 +245,7 @@ const { copyToClipboard } = useClipboard()
|
||||
// 状态
|
||||
const loading = ref(false)
|
||||
const models = ref<Model[]>([])
|
||||
const mappingPreview = ref<ProviderMappingPreviewResponse | null>(null)
|
||||
const togglingModelId = ref<string | null>(null)
|
||||
const testingModelId = ref<string | null>(null)
|
||||
const formatMenuModelId = ref<string | null>(null)
|
||||
@@ -265,11 +272,19 @@ async function copyModelId(modelId: string) {
|
||||
await copyToClipboard(modelId)
|
||||
}
|
||||
|
||||
// 加载模型
|
||||
// 加载模型和映射预览
|
||||
async function loadModels() {
|
||||
try {
|
||||
loading.value = true
|
||||
models.value = await getProviderModels(props.provider.id)
|
||||
const [modelsData, previewData] = await Promise.all([
|
||||
getProviderModels(props.provider.id),
|
||||
getProviderMappingPreview(props.provider.id).catch((err) => {
|
||||
console.warn('Failed to load mapping preview:', err)
|
||||
return null
|
||||
})
|
||||
])
|
||||
models.value = modelsData
|
||||
mappingPreview.value = previewData
|
||||
} catch (err: any) {
|
||||
showError(err.response?.data?.detail || '加载失败', '错误')
|
||||
} finally {
|
||||
@@ -382,6 +397,31 @@ async function toggleModelActive(model: Model) {
|
||||
}
|
||||
}
|
||||
|
||||
// 查找模型的正则映射信息(返回第一个匹配的活跃 key 和映射名称)
|
||||
function findRegexMapping(model: Model): { keyId: string; mappedName: string } | null {
|
||||
if (!mappingPreview.value) return null
|
||||
|
||||
// 在映射预览中查找该模型的全局模型 ID
|
||||
const globalModelId = model.global_model_id
|
||||
if (!globalModelId) return null
|
||||
|
||||
for (const keyInfo of mappingPreview.value.keys) {
|
||||
// 跳过未激活的 key
|
||||
if (!keyInfo.is_active) continue
|
||||
|
||||
for (const gm of keyInfo.matching_global_models) {
|
||||
if (gm.global_model_id === globalModelId && gm.matched_models.length > 0) {
|
||||
// 返回第一个匹配的映射名称
|
||||
return {
|
||||
keyId: keyInfo.key_id,
|
||||
mappedName: gm.matched_models[0].allowed_model
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 测试模型连接性
|
||||
async function testModelConnection(model: Model, apiFormat?: string) {
|
||||
if (testingModelId.value) return
|
||||
@@ -389,11 +429,17 @@ async function testModelConnection(model: Model, apiFormat?: string) {
|
||||
testingModelId.value = model.id
|
||||
formatMenuModelId.value = null
|
||||
try {
|
||||
// 检查是否有正则映射,如果有则使用映射名称和指定 key
|
||||
const regexMapping = findRegexMapping(model)
|
||||
const modelName = regexMapping?.mappedName || model.provider_model_name
|
||||
const apiKeyId = regexMapping?.keyId
|
||||
|
||||
const result = await testModel({
|
||||
provider_id: props.provider.id,
|
||||
model_name: model.provider_model_name,
|
||||
model_name: modelName,
|
||||
message: "hello",
|
||||
api_format: apiFormat
|
||||
api_format: apiFormat,
|
||||
api_key_id: apiKeyId
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
@@ -404,7 +450,7 @@ async function testModelConnection(model: Model, apiFormat?: string) {
|
||||
} else if (result.data?.content_preview) {
|
||||
showSuccess(`流式测试成功,预览: ${result.data.content_preview}`)
|
||||
} else {
|
||||
showSuccess(`模型 "${model.provider_model_name}" 测试成功`)
|
||||
showSuccess(`模型 "${modelName}" 测试成功`)
|
||||
}
|
||||
} else {
|
||||
showError(`模型测试失败: ${parseTestModelError(result)}`)
|
||||
@@ -447,4 +493,9 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({
|
||||
reload: loadModels
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user