mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 03:09:50 +08:00
feat(test,quota,failover): 模型并发测试、统一配额读取器与故障转移取消支持
- 新增 QuotaReader 抽象层,统一 Codex/Kiro/Antigravity 配额解析逻辑,
替换 pool/routes.py 中分散的配额构建函数
- 模型测试支持并发执行多候选,前端新增 useModelTest composable 统一
ModelsTab 和 ModelMappingTab 的测试逻辑
- ModelTestDialog 增加结果概览摘要、超长结果折叠、端点列和新状态支持,
删除已合并的 TestResultDialog
- FailoverEngine 新增客户端断开检测,支持取消剩余候选并标记记录
- 刷新配额改为分批执行,直连测试候选按可用性排序
- 修复 error 判断从 "error" in dict 改为 dict.get("error") 避免误判
This commit is contained in:
@@ -308,31 +308,35 @@
|
||||
@cancel="deleteConfirmOpen = false"
|
||||
/>
|
||||
|
||||
<!-- 测试结果对话框(仅失败时显示) -->
|
||||
<TestResultDialog
|
||||
:result="testResult"
|
||||
<!-- 模型测试对话框 -->
|
||||
<ModelTestDialog
|
||||
:open="modelTest.dialogOpen.value"
|
||||
:result="modelTest.testResult.value"
|
||||
mode="direct"
|
||||
@close="testResult = null"
|
||||
:selecting-model-name="testingModelName"
|
||||
:testing="modelTest.testing.value"
|
||||
:trace="modelTest.testTrace.value"
|
||||
:request-id="modelTest.requestId.value"
|
||||
@close="handleTestDialogClose"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useSmartPagination } from '@/composables/useSmartPagination'
|
||||
import { useModelTest } from '@/composables/useModelTest'
|
||||
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 TestResultDialog from './TestResultDialog.vue'
|
||||
import ModelTestDialog from './ModelTestDialog.vue'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import {
|
||||
testModelFailover,
|
||||
type Model,
|
||||
type ProviderModelAlias,
|
||||
type ProviderMappingPreviewResponse,
|
||||
type TestModelFailoverResponse
|
||||
} from '@/api/endpoints'
|
||||
import { type EndpointAPIKey } from '@/api/endpoints/keys'
|
||||
import { updateModel } from '@/api/endpoints/models'
|
||||
@@ -376,6 +380,9 @@ const emit = defineEmits<{
|
||||
|
||||
const { error: showError, success: showSuccess } = useToast()
|
||||
|
||||
// 模型测试 composable
|
||||
const modelTest = useModelTest({ providerId: () => props.provider.id })
|
||||
|
||||
// 状态
|
||||
const loading = ref(false)
|
||||
const dialogOpen = ref(false)
|
||||
@@ -383,7 +390,7 @@ const deleteConfirmOpen = ref(false)
|
||||
const editingGroup = ref<AliasGroup | null>(null)
|
||||
const deletingGroup = ref<AliasGroup | null>(null)
|
||||
const testingMapping = ref<string | null>(null)
|
||||
const testResult = ref<TestModelFailoverResponse | null>(null)
|
||||
const testingModelName = ref<string | null>(null)
|
||||
const preselectedModelId = ref<string | null>(null)
|
||||
|
||||
// 使用 props 传入的数据
|
||||
@@ -617,30 +624,25 @@ async function onDialogSaved() {
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
// 测试映射(直连测试,带故障转移)
|
||||
function handleTestDialogClose() {
|
||||
modelTest.resetState()
|
||||
testingModelName.value = null
|
||||
}
|
||||
|
||||
// 测试映射(直连测试,带故障转移和实时进度)
|
||||
async function runMappingTest(testingKey: string, modelName: string) {
|
||||
testingMapping.value = testingKey
|
||||
|
||||
try {
|
||||
const result = await testModelFailover({
|
||||
provider_id: props.provider.id,
|
||||
mode: 'direct',
|
||||
model_name: modelName,
|
||||
message: "hello",
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
const successAttempt = result.attempts.find(a => a.status === 'success')
|
||||
const latency = successAttempt?.latency_ms != null ? ` (${successAttempt.latency_ms}ms)` : ''
|
||||
showSuccess(`映射 "${modelName}" 测试成功${latency}`)
|
||||
} else {
|
||||
testResult.value = result
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
showError(`映射测试失败: ${parseApiError(err, '测试请求失败')}`)
|
||||
} finally {
|
||||
testingMapping.value = null
|
||||
}
|
||||
testingModelName.value = modelName
|
||||
await modelTest.startTest({
|
||||
mode: 'direct',
|
||||
modelName,
|
||||
displayLabel: `映射 "${modelName}"`,
|
||||
message: 'hello',
|
||||
onSuccess: () => {
|
||||
testingModelName.value = null
|
||||
},
|
||||
})
|
||||
testingMapping.value = null
|
||||
}
|
||||
|
||||
// 测试精确映射
|
||||
|
||||
Reference in New Issue
Block a user