refactor: 重构限流系统和健康监控,支持按 API 格式区分

- 将 adaptive_concurrency 重命名为 adaptive_rpm,从并发控制改为 RPM 控制
- 健康监控器支持按 API 格式独立管理健康度和熔断器状态
- 新增 model_permissions 模块,支持按格式配置允许的模型
- 重构前端提供商相关表单组件,新增 Collapsible UI 组件
- 新增数据库迁移脚本支持新的数据结构
This commit is contained in:
fawney19
2026-01-10 18:43:53 +08:00
parent dd2fbf4424
commit 09e0f594ff
97 changed files with 6642 additions and 4169 deletions

View File

@@ -20,7 +20,7 @@ interface ValidationError {
const fieldNameMap: Record<string, string> = {
'api_key': 'API 密钥',
'priority': '优先级',
'max_concurrent': '最大并发',
'rpm_limit': 'RPM 限制',
'rate_limit': '速率限制',
'daily_limit': '每日限制',
'monthly_limit': '每月限制',
@@ -44,7 +44,6 @@ const fieldNameMap: Record<string, string> = {
'monthly_quota_usd': '月度配额',
'quota_reset_day': '配额重置日',
'quota_expires_at': '配额过期时间',
'rpm_limit': 'RPM 限制',
'cache_ttl_minutes': '缓存 TTL',
'max_probe_interval_minutes': '最大探测间隔',
}
@@ -151,11 +150,18 @@ export function parseApiError(err: unknown, defaultMessage: string = '操作失
return '无法连接到服务器,请检查网络连接'
}
const detail = err.response?.data?.detail
const data = err.response?.data
// 1. 处理 {error: {type, message}} 格式ProxyException 返回格式)
if (data?.error?.message) {
return data.error.message
}
const detail = data?.detail
// 如果没有 detail 字段
if (!detail) {
return err.response?.data?.message || err.message || defaultMessage
return data?.message || err.message || defaultMessage
}
// 1. 处理 Pydantic 验证错误(数组格式)

View File

@@ -54,6 +54,57 @@ export function parseNumberInput(
return result
}
/**
* Parse number input value for nullable fields (like rpm_limit)
* Returns `null` when empty (to signal "use adaptive/default mode")
* Returns `undefined` when not provided (to signal "keep original value")
*
* @param value - Input value (string or number)
* @param options - Parse options
* @returns Parsed number, null (for empty/adaptive), or undefined
*/
export function parseNullableNumberInput(
value: string | number | null | undefined,
options: {
allowFloat?: boolean
min?: number
max?: number
} = {}
): number | null | undefined {
const { allowFloat = false, min, max } = options
// Empty string means "null" (adaptive mode)
if (value === '') {
return null
}
// null/undefined means "keep original value"
if (value === null || value === undefined) {
return undefined
}
// Parse the value
const num = typeof value === 'string'
? (allowFloat ? parseFloat(value) : parseInt(value, 10))
: value
// Handle NaN - treat as null (adaptive mode)
if (isNaN(num)) {
return null
}
// Apply min/max constraints
let result = num
if (min !== undefined && result < min) {
result = min
}
if (max !== undefined && result > max) {
result = max
}
return result
}
/**
* Create a handler function for number input with specific field
* Useful for creating inline handlers in templates