mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-12 04:28:28 +08:00
feat(ui): 优化上游模型查询错误提示
新增 parseUpstreamModelError 函数,将后端返回的原始错误信息 转换为用户友好的中文提示,支持解析 HTTP 状态码和 JSON 错误体
This commit is contained in:
@@ -187,6 +187,7 @@ import Button from '@/components/ui/button.vue'
|
||||
import Badge from '@/components/ui/badge.vue'
|
||||
import Checkbox from '@/components/ui/checkbox.vue'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { parseUpstreamModelError } from '@/utils/errorParser'
|
||||
import { adminApi } from '@/api/admin'
|
||||
import {
|
||||
importModelsFromUpstream,
|
||||
@@ -289,13 +290,18 @@ async function fetchUpstreamModels() {
|
||||
hasQueried.value = true
|
||||
// 如果有部分失败,显示警告提示
|
||||
if (response.data.error) {
|
||||
showError(`部分格式获取失败: ${response.data.error}`, '警告')
|
||||
// 使用友好的错误解析
|
||||
showError(`部分格式获取失败: ${parseUpstreamModelError(response.data.error)}`, '警告')
|
||||
}
|
||||
} else {
|
||||
errorMessage.value = response.data?.error || '获取上游模型失败'
|
||||
// 使用友好的错误解析
|
||||
const rawError = response.data?.error || '获取上游模型失败'
|
||||
errorMessage.value = parseUpstreamModelError(rawError)
|
||||
}
|
||||
} catch (err: any) {
|
||||
errorMessage.value = err.response?.data?.detail || '获取上游模型失败'
|
||||
// 使用友好的错误解析
|
||||
const rawError = err.response?.data?.detail || err.message || '获取上游模型失败'
|
||||
errorMessage.value = parseUpstreamModelError(rawError)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
import { Dialog, Button, Input, Checkbox, Badge } from '@/components/ui'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { parseApiError } from '@/utils/errorParser'
|
||||
import { parseApiError, parseUpstreamModelError } from '@/utils/errorParser'
|
||||
import {
|
||||
updateProviderKey,
|
||||
API_FORMAT_LABELS,
|
||||
@@ -522,11 +522,17 @@ async function fetchUpstreamModels() {
|
||||
}
|
||||
collapsedGroups.value = allGroups
|
||||
} else {
|
||||
showError(response.data?.error || '获取上游模型失败', '错误')
|
||||
// 使用友好的错误解析
|
||||
const errorMsg = response.data?.error
|
||||
? parseUpstreamModelError(response.data.error)
|
||||
: '获取上游模型失败'
|
||||
showError(errorMsg, '获取上游模型失败')
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (loadingCancelled) return
|
||||
showError(err.response?.data?.detail || '获取上游模型失败', '错误')
|
||||
// 使用友好的错误解析
|
||||
const rawError = err.response?.data?.detail || err.message || '获取上游模型失败'
|
||||
showError(parseUpstreamModelError(rawError), '获取上游模型失败')
|
||||
} finally {
|
||||
fetchingUpstreamModels.value = false
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import { ref } from 'vue'
|
||||
import { adminApi } from '@/api/admin'
|
||||
import { parseUpstreamModelError } from '@/utils/errorParser'
|
||||
import type { UpstreamModel } from '@/api/endpoints/types'
|
||||
|
||||
// 扩展类型,包含可能的额外字段
|
||||
@@ -63,10 +64,14 @@ export function useUpstreamModelsCache() {
|
||||
})
|
||||
return { models: response.data.models }
|
||||
} else {
|
||||
return { models: [], error: response.data?.error || '获取上游模型失败' }
|
||||
// 使用友好的错误解析
|
||||
const rawError = response.data?.error || '获取上游模型失败'
|
||||
return { models: [], error: parseUpstreamModelError(rawError) }
|
||||
}
|
||||
} catch (err: any) {
|
||||
return { models: [], error: err.response?.data?.detail || '获取上游模型失败' }
|
||||
// 使用友好的错误解析
|
||||
const rawError = err.response?.data?.detail || err.message || '获取上游模型失败'
|
||||
return { models: [], error: parseUpstreamModelError(rawError) }
|
||||
} finally {
|
||||
loadingMap.value.set(providerId, false)
|
||||
pendingRequests.delete(providerId)
|
||||
|
||||
@@ -250,3 +250,115 @@ export function parseTestModelError(result: {
|
||||
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析上游模型查询错误信息
|
||||
* 将后端返回的原始错误信息(如 "HTTP 401: {json...}")转换为友好的错误提示
|
||||
* @param error 错误字符串,格式可能是 "HTTP {status}: {json_body}" 或其他
|
||||
* @returns 友好的错误信息
|
||||
*/
|
||||
export function parseUpstreamModelError(error: string): string {
|
||||
if (!error) return '获取上游模型失败'
|
||||
|
||||
// 匹配 "HTTP {status}: {body}" 格式
|
||||
const httpMatch = error.match(/^HTTP\s+(\d+):\s*(.*)$/s)
|
||||
if (httpMatch) {
|
||||
const status = parseInt(httpMatch[1], 10)
|
||||
const body = httpMatch[2]
|
||||
|
||||
// 根据状态码生成友好消息
|
||||
let friendlyMsg = ''
|
||||
if (status === 401) {
|
||||
friendlyMsg = '密钥无效或已过期'
|
||||
} else if (status === 403) {
|
||||
friendlyMsg = '密钥权限不足'
|
||||
} else if (status === 404) {
|
||||
friendlyMsg = '模型列表接口不存在'
|
||||
} else if (status === 429) {
|
||||
friendlyMsg = '请求频率过高,请稍后重试'
|
||||
} else if (status >= 500) {
|
||||
friendlyMsg = '上游服务暂时不可用'
|
||||
}
|
||||
|
||||
// 尝试从 JSON body 中提取更详细的错误信息
|
||||
if (body) {
|
||||
try {
|
||||
const parsed = JSON.parse(body)
|
||||
// 常见的错误格式: {error: {message: "..."}} 或 {error: "..."} 或 {message: "..."}
|
||||
let detailMsg = ''
|
||||
if (parsed.error?.message) {
|
||||
detailMsg = parsed.error.message
|
||||
} else if (typeof parsed.error === 'string') {
|
||||
detailMsg = parsed.error
|
||||
} else if (parsed.message) {
|
||||
detailMsg = parsed.message
|
||||
} else if (parsed.detail) {
|
||||
detailMsg = typeof parsed.detail === 'string' ? parsed.detail : JSON.stringify(parsed.detail)
|
||||
}
|
||||
|
||||
// 如果提取到了详细消息,用它来丰富友好消息
|
||||
if (detailMsg) {
|
||||
// 检查是否是 token/认证相关的错误
|
||||
const lowerMsg = detailMsg.toLowerCase()
|
||||
if (lowerMsg.includes('invalid token') || lowerMsg.includes('invalid api key')) {
|
||||
return '密钥无效,请检查密钥是否正确'
|
||||
}
|
||||
if (lowerMsg.includes('expired')) {
|
||||
return '密钥已过期,请更新密钥'
|
||||
}
|
||||
if (lowerMsg.includes('quota') || lowerMsg.includes('exceeded')) {
|
||||
return '配额已用尽或超出限制'
|
||||
}
|
||||
if (lowerMsg.includes('rate limit')) {
|
||||
return '请求频率过高,请稍后重试'
|
||||
}
|
||||
// 没有匹配特定关键词,但有详细信息,使用它作为补充
|
||||
if (friendlyMsg) {
|
||||
const truncated = detailMsg.length > 80 ? detailMsg.substring(0, 80) + '...' : detailMsg
|
||||
return `${friendlyMsg}: ${truncated}`
|
||||
}
|
||||
// 没有友好消息,直接使用详细信息
|
||||
const truncated = detailMsg.length > 100 ? detailMsg.substring(0, 100) + '...' : detailMsg
|
||||
return truncated
|
||||
}
|
||||
} catch {
|
||||
// JSON 解析失败,忽略
|
||||
}
|
||||
}
|
||||
|
||||
// 返回友好消息,附加状态码
|
||||
if (friendlyMsg) {
|
||||
return friendlyMsg
|
||||
}
|
||||
return `请求失败 (HTTP ${status})`
|
||||
}
|
||||
|
||||
// 检查是否是请求错误
|
||||
if (error.startsWith('Request error:')) {
|
||||
const detail = error.replace('Request error:', '').trim()
|
||||
if (detail.toLowerCase().includes('timeout')) {
|
||||
return '请求超时,上游服务响应过慢'
|
||||
}
|
||||
if (detail.toLowerCase().includes('connection')) {
|
||||
return '无法连接到上游服务'
|
||||
}
|
||||
return '网络请求失败'
|
||||
}
|
||||
|
||||
// 检查是否是未知 API 格式
|
||||
if (error.startsWith('Unknown API format:')) {
|
||||
return '不支持的 API 格式'
|
||||
}
|
||||
|
||||
// 如果包含分号,可能是多个错误合并的,取第一个
|
||||
if (error.includes('; ')) {
|
||||
const firstError = error.split('; ')[0]
|
||||
return parseUpstreamModelError(firstError)
|
||||
}
|
||||
|
||||
// 默认返回原始错误(截断过长的部分)
|
||||
if (error.length > 100) {
|
||||
return error.substring(0, 100) + '...'
|
||||
}
|
||||
return error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user