mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 08:12:26 +08:00
feat: 引入统一的端点检查器以重构适配器并改进错误处理和用量统计。
This commit is contained in:
@@ -110,16 +110,30 @@
|
||||
<div
|
||||
v-for="mapping in group.aliases"
|
||||
:key="mapping.name"
|
||||
class="flex items-center gap-2 py-1"
|
||||
class="flex items-center justify-between gap-2 py-1"
|
||||
>
|
||||
<!-- 优先级标签 -->
|
||||
<span class="inline-flex items-center justify-center w-5 h-5 rounded bg-background border text-xs font-medium shrink-0">
|
||||
{{ mapping.priority }}
|
||||
</span>
|
||||
<!-- 映射名称 -->
|
||||
<span class="font-mono text-sm truncate">
|
||||
{{ mapping.name }}
|
||||
</span>
|
||||
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||
<!-- 优先级标签 -->
|
||||
<span class="inline-flex items-center justify-center w-5 h-5 rounded bg-background border text-xs font-medium shrink-0">
|
||||
{{ mapping.priority }}
|
||||
</span>
|
||||
<!-- 映射名称 -->
|
||||
<span class="font-mono text-sm truncate">
|
||||
{{ mapping.name }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 测试按钮 -->
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-7 w-7 shrink-0"
|
||||
title="测试映射"
|
||||
:disabled="testingMapping === `${group.model.id}-${group.apiFormatsKey}-${mapping.name}`"
|
||||
@click="testMapping(group, mapping)"
|
||||
>
|
||||
<Loader2 v-if="testingMapping === `${group.model.id}-${group.apiFormatsKey}-${mapping.name}`" class="w-3 h-3 animate-spin" />
|
||||
<Play v-else class="w-3 h-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -166,13 +180,14 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { Tag, Plus, Edit, Trash2, ChevronRight } from 'lucide-vue-next'
|
||||
import { Tag, Plus, Edit, Trash2, ChevronRight, Loader2, Play } from 'lucide-vue-next'
|
||||
import { Card, Button, Badge } from '@/components/ui'
|
||||
import AlertDialog from '@/components/common/AlertDialog.vue'
|
||||
import ModelMappingDialog, { type AliasGroup } from '../ModelMappingDialog.vue'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import {
|
||||
getProviderModels,
|
||||
testModel,
|
||||
API_FORMAT_LABELS,
|
||||
type Model,
|
||||
type ProviderModelAlias
|
||||
@@ -196,6 +211,7 @@ const dialogOpen = ref(false)
|
||||
const deleteConfirmOpen = ref(false)
|
||||
const editingGroup = ref<AliasGroup | null>(null)
|
||||
const deletingGroup = ref<AliasGroup | null>(null)
|
||||
const testingMapping = ref<string | null>(null)
|
||||
|
||||
// 列表展开状态
|
||||
const expandedAliasGroups = ref<Set<string>>(new Set())
|
||||
@@ -337,6 +353,81 @@ async function onDialogSaved() {
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
// 测试模型映射
|
||||
async function testMapping(group: any, mapping: any) {
|
||||
const testingKey = `${group.model.id}-${group.apiFormatsKey}-${mapping.name}`
|
||||
testingMapping.value = testingKey
|
||||
|
||||
try {
|
||||
// 根据分组的 API 格式来确定应该使用的格式
|
||||
let apiFormat = null
|
||||
if (group.apiFormats.length === 1) {
|
||||
apiFormat = group.apiFormats[0]
|
||||
} else if (group.apiFormats.length === 0) {
|
||||
// 如果没有指定格式,但分组显示为"全部",则使用模型的默认格式
|
||||
apiFormat = group.model.effective_api_format || group.model.api_format
|
||||
}
|
||||
|
||||
console.log(`测试映射 ${mapping.name},使用 API Format: ${apiFormat}`)
|
||||
|
||||
const result = await testModel({
|
||||
provider_id: props.provider.id,
|
||||
model_name: mapping.name, // 使用映射名称进行测试
|
||||
message: "hello",
|
||||
api_format: apiFormat
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
showSuccess(`映射 "${mapping.name}" 测试成功`)
|
||||
|
||||
// 如果有响应内容,可以显示更多信息
|
||||
if (result.data?.response?.choices?.[0]?.message?.content) {
|
||||
const content = result.data.response.choices[0].message.content
|
||||
showSuccess(`测试成功,响应: ${content.substring(0, 100)}${content.length > 100 ? '...' : ''}`)
|
||||
} else if (result.data?.content_preview) {
|
||||
showSuccess(`流式测试成功,预览: ${result.data.content_preview}`)
|
||||
}
|
||||
} else {
|
||||
// 根据不同的错误类型显示更详细的信息
|
||||
let errorMsg = result.error || '测试失败'
|
||||
|
||||
// 检查HTTP状态码错误
|
||||
if (result.data?.response?.status_code) {
|
||||
const status = result.data.response.status_code
|
||||
if (status === 403) {
|
||||
errorMsg = '认证失败: API密钥无效或客户端类型不被允许'
|
||||
} else if (status === 401) {
|
||||
errorMsg = '认证失败: API密钥无效或已过期'
|
||||
} else if (status === 404) {
|
||||
errorMsg = '模型不存在: 请检查模型名称是否正确'
|
||||
} else if (status === 429) {
|
||||
errorMsg = '请求频率过高: 请稍后重试'
|
||||
} else if (status >= 500) {
|
||||
errorMsg = `服务器错误: HTTP ${status}`
|
||||
} else {
|
||||
errorMsg = `请求失败: HTTP ${status}`
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从错误响应中提取更多信息
|
||||
if (result.data?.response?.error) {
|
||||
if (typeof result.data.response.error === 'string') {
|
||||
errorMsg = result.data.response.error
|
||||
} else if (result.data.response.error?.message) {
|
||||
errorMsg = result.data.response.error.message
|
||||
}
|
||||
}
|
||||
|
||||
showError(`映射测试失败: ${errorMsg}`)
|
||||
}
|
||||
} catch (err: any) {
|
||||
const errorMsg = err.response?.data?.detail || err.message || '测试请求失败'
|
||||
showError(`映射测试失败: ${errorMsg}`)
|
||||
} finally {
|
||||
testingMapping.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 provider 变化
|
||||
watch(() => props.provider?.id, (newId) => {
|
||||
if (newId) {
|
||||
@@ -349,4 +440,9 @@ onMounted(() => {
|
||||
loadModels()
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露给父组件,用于检测是否有弹窗打开
|
||||
defineExpose({
|
||||
dialogOpen: computed(() => dialogOpen.value || deleteConfirmOpen.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user