update at 2025-11-01 17:57:04
This commit is contained in:
@@ -242,7 +242,7 @@
|
||||
<div class="progress-info">
|
||||
<p>
|
||||
<strong>当前进度:</strong>
|
||||
{{ healthCheckResult.progress.current }} / {{ healthCheckResult.progress.total }}
|
||||
{{ healthCheckResult.progress.current }}项 / {{ healthCheckResult.progress.total }}项
|
||||
</p>
|
||||
<p><strong>当前模型:</strong> {{ healthCheckResult.progress.modelId }}</p>
|
||||
</div>
|
||||
@@ -250,8 +250,13 @@
|
||||
type="line"
|
||||
:percentage="healthCheckResult.progress.total > 0 ?
|
||||
(healthCheckResult.progress.current / healthCheckResult.progress.total * 100) : 0"
|
||||
:show-indicator="true"
|
||||
:show-indicator="false"
|
||||
/>
|
||||
<div class="progress-details">
|
||||
<span>{{ healthCheckResult.progress.current }}项</span>
|
||||
<span>{{ healthCheckResult.progress.total }}项</span>
|
||||
<span>{{ healthCheckResult.progress.current > 0 ? Math.round((healthCheckResult.progress.current / healthCheckResult.progress.total) * 100) : 0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="healthCheckResult.status === 'success'" class="check-success">
|
||||
@@ -262,7 +267,7 @@
|
||||
<div class="summary-card available">
|
||||
<div class="card-icon">✓</div>
|
||||
<div class="card-info">
|
||||
<div class="card-number">{{ healthCheckResult.availableModels.length }}</div>
|
||||
<div class="card-number">{{ healthCheckResult.availableModels.length }}个</div>
|
||||
<div class="card-label">可用模型</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -270,7 +275,7 @@
|
||||
<div class="summary-card unavailable">
|
||||
<div class="card-icon">✗</div>
|
||||
<div class="card-info">
|
||||
<div class="card-number">{{ healthCheckResult.unavailableModels.length }}</div>
|
||||
<div class="card-number">{{ healthCheckResult.unavailableModels.length }}个</div>
|
||||
<div class="card-label">不可用模型</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -279,7 +284,7 @@
|
||||
<!-- 详细结果 -->
|
||||
<div class="detailed-results">
|
||||
<div v-if="healthCheckResult.availableModels.length > 0" class="result-section">
|
||||
<h4>✓ 可用模型 ({{ healthCheckResult.availableModels.length }})</h4>
|
||||
<h4>✓ 可用模型 ({{ healthCheckResult.availableModels.length }}个)</h4>
|
||||
<div class="model-list">
|
||||
<n-tag
|
||||
v-for="result in healthCheckResult.results.filter(r => r.available)"
|
||||
@@ -295,7 +300,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="healthCheckResult.unavailableModels.length > 0" class="result-section">
|
||||
<h4>✗ 不可用模型 ({{ healthCheckResult.unavailableModels.length }})</h4>
|
||||
<h4>✗ 不可用模型 ({{ healthCheckResult.unavailableModels.length }}个)</h4>
|
||||
<div class="model-list">
|
||||
<n-tag
|
||||
v-for="result in healthCheckResult.results.filter(r => !r.available)"
|
||||
@@ -539,6 +544,7 @@ const healthCheckModels = async (service: ModelService) => {
|
||||
const result = await modelServiceManager.healthCheckAllModels(
|
||||
service,
|
||||
(current, total, modelId) => {
|
||||
// 直接更新进度,不使用防抖
|
||||
healthCheckResult.progress = { current, total, modelId }
|
||||
}
|
||||
)
|
||||
@@ -1104,6 +1110,21 @@ onMounted(() => {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.progress-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-details span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.check-success h3 {
|
||||
margin: 16px 0;
|
||||
color: #18a058;
|
||||
|
||||
@@ -919,6 +919,124 @@ export class ModelServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 带自定义超时的聊天请求(用于健康检测)
|
||||
private async makeChatRequestWithTimeout(service: ModelService, messages: any[], model: string, timeoutMs: number): Promise<any> {
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
|
||||
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
let url = ''
|
||||
let body: any = {}
|
||||
|
||||
// 构建请求(与makeChatRequest相同的逻辑)
|
||||
switch (service.type) {
|
||||
case 'openai':
|
||||
case 'local':
|
||||
headers['Authorization'] = `Bearer ${service.apiKey}`
|
||||
url = `${service.url}/chat/completions`
|
||||
body = {
|
||||
model,
|
||||
messages,
|
||||
stream: false
|
||||
}
|
||||
break
|
||||
|
||||
case 'dashscope':
|
||||
headers['Authorization'] = `Bearer ${service.apiKey}`
|
||||
url = `${service.url}/chat/completions`
|
||||
body = {
|
||||
model,
|
||||
messages,
|
||||
stream: false,
|
||||
parameters: {}
|
||||
}
|
||||
break
|
||||
|
||||
case 'volcengine':
|
||||
headers['Authorization'] = `Bearer ${service.apiKey}`
|
||||
url = `${service.url}/chat/completions`
|
||||
body = {
|
||||
model,
|
||||
messages,
|
||||
stream: false
|
||||
}
|
||||
break
|
||||
|
||||
case 'claude':
|
||||
headers['x-api-key'] = service.apiKey
|
||||
headers['anthropic-version'] = '2023-06-01'
|
||||
url = `${service.url}/messages`
|
||||
body = {
|
||||
model,
|
||||
messages: this.convertToClaudeFormat(messages),
|
||||
max_tokens: 4096
|
||||
}
|
||||
break
|
||||
|
||||
case 'gemini':
|
||||
url = `${service.url}/models/${model}:generateContent?key=${service.apiKey}`
|
||||
body = {
|
||||
contents: this.convertToGeminiFormat(messages)
|
||||
}
|
||||
break
|
||||
|
||||
case 'azure':
|
||||
headers['api-key'] = service.apiKey
|
||||
url = `${service.url}/openai/deployments/${model}/chat/completions?api-version=2023-12-01-preview`
|
||||
body = {
|
||||
messages,
|
||||
stream: false
|
||||
}
|
||||
break
|
||||
|
||||
case 'custom':
|
||||
try {
|
||||
const config = JSON.parse(service.customConfig || '{}')
|
||||
Object.assign(headers, config.headers || {})
|
||||
} catch (e) {
|
||||
console.warn('自定义配置解析失败:', e)
|
||||
}
|
||||
url = `${service.url}/chat/completions`
|
||||
body = {
|
||||
model,
|
||||
messages,
|
||||
stream: false
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error(`不支持的服务类型: ${service.type}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
signal: controller.signal
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`)
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
return result
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId)
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
throw new Error(`检测超时(${timeoutMs / 1000}秒)`)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检测 - 测试单个模型是否可用
|
||||
async testModelHealth(service: ModelService, modelId: string): Promise<{
|
||||
modelId: string
|
||||
@@ -929,14 +1047,10 @@ export class ModelServiceManager {
|
||||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
// 发送一个最小的测试请求
|
||||
const result = await this.sendChatRequest(service.id, [
|
||||
// 使用3秒超时进行健康检测(简化版)
|
||||
await this.makeChatRequestWithTimeout(service, [
|
||||
{ role: 'user', content: 'hi' }
|
||||
], modelId)
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || '测试失败')
|
||||
}
|
||||
], modelId, 3000)
|
||||
|
||||
const latency = Date.now() - startTime
|
||||
return {
|
||||
@@ -945,9 +1059,11 @@ export class ModelServiceManager {
|
||||
latency
|
||||
}
|
||||
} catch (error) {
|
||||
const latency = Date.now() - startTime
|
||||
return {
|
||||
modelId,
|
||||
available: false,
|
||||
latency,
|
||||
error: error instanceof Error ? error.message : '测试失败'
|
||||
}
|
||||
}
|
||||
@@ -975,24 +1091,34 @@ export class ModelServiceManager {
|
||||
error?: string
|
||||
}> = []
|
||||
|
||||
// 初始进度
|
||||
if (onProgress) {
|
||||
onProgress(0, models.length, '准备开始检测...')
|
||||
}
|
||||
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
const modelId = models[i]
|
||||
|
||||
// 通知进度
|
||||
// 开始检测当前模型时通知进度
|
||||
if (onProgress) {
|
||||
onProgress(i + 1, models.length, modelId)
|
||||
onProgress(i, models.length, `正在检测: ${modelId}`)
|
||||
}
|
||||
|
||||
// 测试模型健康状态
|
||||
const result = await this.testModelHealth(service, modelId)
|
||||
results.push(result)
|
||||
|
||||
// 添加小延迟避免过快请求
|
||||
if (i < models.length - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
// 检测完成后更新进度
|
||||
if (onProgress) {
|
||||
onProgress(i + 1, models.length, `已完成: ${modelId}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 最终进度
|
||||
if (onProgress) {
|
||||
onProgress(models.length, models.length, '检测完成')
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
const availableModels = results.filter(r => r.available).map(r => r.modelId)
|
||||
const unavailableModels = results.filter(r => !r.available).map(r => r.modelId)
|
||||
|
||||
Reference in New Issue
Block a user