mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
feat(admin,pool,billing): 端点级模型测试、Provider 自动置顶、缓存 TTL 分级计费展示与账号状态增强
- 模型测试支持指定端点:新增 ModelTestDialog 组件,多端点时弹窗选择,单端点直接测试; 后端 test-model-failover 接口新增 endpoint_id 参数,支持 global/direct 模式下按端点过滤候选 - 创建 Provider 时优先级自动置顶(provider_priority 默认 None,后端取 min-1), 显式指定优先级时 shift 已有行;前端创建时不发送 priority,更新时保留 - 缓存计费 UI 增强:RequestDetailDrawer 支持 5min/1h 缓存创建 token 分级展示, 含按 TTL 匹配单价和分行成本计算;ModelDetailDrawer/ModelsTab 标签区分 5min/1h 缓存创建 - Pool 批量操作额度筛选拆分为「无5H限额」和「无周限额」,按 | 分隔 segment 匹配 - KeyFormDialog 优化非 vertex_ai 时布局,API 密钥输入内联到 grid 右列 - Codex refresher 结构化错误标记:401/402/403 使用 [OAUTH_EXPIRED]/[ACCOUNT_BLOCK] 前缀, 新增 deactivated_workspace 识别与分类 - 前后端 accountBlock 关键词同步:新增 token invalidated、deactivated_workspace 识别, OAuth 失效提示清理 block 前缀后展示 - PoolConfig 新增 batch_concurrency 配置(默认 8,上限 32) - 预设模型新增 gpt-5.4;TestResultDialog 响应式布局与 key 脱敏优化
This commit is contained in:
@@ -273,7 +273,8 @@ import { useProxyNodesStore } from '@/stores/proxy-nodes'
|
||||
|
||||
type QuickSelectorValue =
|
||||
| 'banned'
|
||||
| 'no_quota'
|
||||
| 'no_5h_limit'
|
||||
| 'no_weekly_limit'
|
||||
| 'plan_free'
|
||||
| 'plan_team'
|
||||
| 'oauth_invalid'
|
||||
@@ -305,7 +306,8 @@ const emit = defineEmits<{
|
||||
|
||||
const QUICK_SELECT_OPTIONS: Array<{ value: QuickSelectorValue; label: string }> = [
|
||||
{ value: 'banned', label: '已封号' },
|
||||
{ value: 'no_quota', label: '无额度' },
|
||||
{ value: 'no_5h_limit', label: '无5H限额' },
|
||||
{ value: 'no_weekly_limit', label: '无周限额' },
|
||||
{ value: 'plan_free', label: '全部 Free' },
|
||||
{ value: 'plan_team', label: '全部 Team' },
|
||||
{ value: 'oauth_invalid', label: 'OAuth 失效' },
|
||||
@@ -408,16 +410,33 @@ function isBannedKey(key: PoolKeyDetail): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
function hasNoQuota(key: PoolKeyDetail): boolean {
|
||||
const quotaText = normalizeText(key.account_quota)
|
||||
if (!quotaText) return false
|
||||
if (/(无额度|额度不足|已耗尽|耗尽|depleted|exhausted|insufficient)/.test(quotaText)) return true
|
||||
if (/剩余\s*0(\.0+)?/.test(quotaText)) return true
|
||||
if (/\b0(\.0+)?\s*\/\s*\d/.test(quotaText)) return true
|
||||
if (/\b0(\.0+)?%/.test(quotaText)) return true
|
||||
function getQuotaSegments(accountQuota: string | null | undefined): string[] {
|
||||
return String(accountQuota || '')
|
||||
.split('|')
|
||||
.map((segment) => normalizeText(segment))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function isDepletedQuotaSegment(segment: string): boolean {
|
||||
if (/(无额度|额度不足|已耗尽|耗尽|depleted|exhausted|insufficient)/.test(segment)) return true
|
||||
if (/剩余\s*0(\.0+)?/.test(segment)) return true
|
||||
if (/\b0(\.0+)?\s*\/\s*\d/.test(segment)) return true
|
||||
if (/\b0(\.0+)?%/.test(segment)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function hasNoFiveHourLimit(key: PoolKeyDetail): boolean {
|
||||
return getQuotaSegments(key.account_quota)
|
||||
.filter((segment) => /5h|5小时/.test(segment))
|
||||
.some(isDepletedQuotaSegment)
|
||||
}
|
||||
|
||||
function hasNoWeeklyLimit(key: PoolKeyDetail): boolean {
|
||||
return getQuotaSegments(key.account_quota)
|
||||
.filter((segment) => /周|weekly|week/.test(segment))
|
||||
.some(isDepletedQuotaSegment)
|
||||
}
|
||||
|
||||
function isOAuthInvalid(key: PoolKeyDetail): boolean {
|
||||
if (normalizeText(key.auth_type) !== 'oauth') return false
|
||||
if (key.oauth_invalid_at != null || normalizeText(key.oauth_invalid_reason)) return true
|
||||
@@ -455,7 +474,8 @@ function toggleSelectFiltered(checked: boolean | 'indeterminate'): void {
|
||||
|
||||
function matchesSelector(key: PoolKeyDetail, selector: QuickSelectorValue): boolean {
|
||||
if (selector === 'banned') return isBannedKey(key)
|
||||
if (selector === 'no_quota') return hasNoQuota(key)
|
||||
if (selector === 'no_5h_limit') return hasNoFiveHourLimit(key)
|
||||
if (selector === 'no_weekly_limit') return hasNoWeeklyLimit(key)
|
||||
if (selector === 'plan_free') return isFreePlan(key)
|
||||
if (selector === 'plan_team') return isTeamPlan(key)
|
||||
if (selector === 'oauth_invalid') return isOAuthInvalid(key)
|
||||
|
||||
Reference in New Issue
Block a user