mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 03:09:50 +08:00
feat: provider api_formats 可空继承、OpenAI 图片 edit/variation 与用量配额多项补强
- 鉴权: provider_api_keys.api_formats 改为可空,OAuth 托管 key 自动继承 provider endpoints 激活格式,相关 handler/测试同步更新 - 图片 planner: OpenAI 图片路由新增 edit/variation 操作并完善参数校验、响应合并与流式处理 - 用量: user me usage 返回区分 client_requested_stream/upstream_is_stream,前端 usage 列表筛选与展示增强 - 统计: stats_daily_model 新增 cache_creation_ephemeral_5m/1h tokens 字段与回填链路 - 配额/observability: quota repository 新增内存与 SQL 扩展,admin observability usage 字段扩充 - 其它: OAuth 导入/轮询收敛、provider 汇总与 pool admin 读写链路小修、新增 system_config 缓存与 provider template handler Closes #318 Co-authored-by: Entropy.Xu <[email protected]>
This commit is contained in:
@@ -1420,21 +1420,23 @@ watch(
|
||||
[() => props.providerId, () => props.open],
|
||||
async ([newId, newOpen], [_oldId, oldOpen]) => {
|
||||
if (newOpen && newId) {
|
||||
if (props.initialProvider?.id === newId) {
|
||||
const hasInitialProvider = props.initialProvider?.id === newId
|
||||
if (hasInitialProvider) {
|
||||
provider.value = props.initialProvider
|
||||
loading.value = false
|
||||
}
|
||||
void loadSystemFormatConversionConfig()
|
||||
// mapping-preview 较慢,不阻塞首屏渲染
|
||||
void loadMappingPreview()
|
||||
await Promise.all([
|
||||
loadProvider(),
|
||||
loadEndpoints(),
|
||||
])
|
||||
const endpointsPromise = loadEndpoints()
|
||||
if (!hasInitialProvider) {
|
||||
await loadProvider()
|
||||
}
|
||||
// 仅在抽屉刚打开时启动倒计时
|
||||
if (newOpen && !oldOpen) {
|
||||
startCountdownTimer()
|
||||
}
|
||||
void autoRefreshQuotaInBackground()
|
||||
void endpointsPromise.then(() => autoRefreshQuotaInBackground())
|
||||
} else if (!newOpen && oldOpen) {
|
||||
// 使在途请求失效,避免关闭后旧响应回写
|
||||
providerLoadRequestId += 1
|
||||
@@ -2705,8 +2707,16 @@ async function handleKeyDrop(event: DragEvent, targetIndex: number) {
|
||||
|
||||
// 获取密钥的 API 格式列表(按指定顺序排序)
|
||||
function getKeyApiFormats(key: EndpointAPIKey, endpoint?: ProviderEndpointWithKeys): string[] {
|
||||
const providerType = provider.value?.provider_type
|
||||
let formats: string[] = []
|
||||
if (key.api_formats && key.api_formats.length > 0) {
|
||||
|
||||
if (
|
||||
providerType
|
||||
&& isOAuthAccountProviderType(providerType)
|
||||
&& isOAuthManagedCredential(key)
|
||||
) {
|
||||
formats = [...availableKeyApiFormats.value]
|
||||
} else if (key.api_formats && key.api_formats.length > 0) {
|
||||
formats = [...key.api_formats]
|
||||
} else if (endpoint) {
|
||||
formats = [endpoint.api_format]
|
||||
|
||||
@@ -5,7 +5,9 @@ import { formatBalanceExtraFromSchema, type CredentialsSchema } from '@/features
|
||||
import type { BalanceExtraItem } from '@/features/providers/auth-templates'
|
||||
import { log } from '@/utils/logger'
|
||||
|
||||
const MAX_BALANCE_RETRIES = 3
|
||||
const MAX_BALANCE_RETRIES = 2
|
||||
const PENDING_BALANCE_RETRY_BASE_DELAY_MS = 12_000
|
||||
const PENDING_BALANCE_RETRY_MAX_DELAY_MS = 60_000
|
||||
|
||||
export function useProviderBalance() {
|
||||
// 余额数据缓存 {providerId: ActionResultResponse}
|
||||
@@ -86,7 +88,7 @@ export function useProviderBalance() {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有 pending 状态的 provider,3秒后自动重试
|
||||
// 如果有 pending 状态的 provider,延后重试,避免把后台刷新队列打成高频轮询
|
||||
if (pendingProviderIds.length > 0) {
|
||||
const timerId = setTimeout(() => {
|
||||
pendingTimers.delete(timerId)
|
||||
@@ -94,7 +96,7 @@ export function useProviderBalance() {
|
||||
if (currentVersion === balanceLoadVersion) {
|
||||
retryPendingBalances(pendingProviderIds, currentVersion, 0)
|
||||
}
|
||||
}, 3000)
|
||||
}, PENDING_BALANCE_RETRY_BASE_DELAY_MS)
|
||||
pendingTimers.add(timerId)
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -118,7 +120,10 @@ export function useProviderBalance() {
|
||||
|
||||
// 如果还有 pending 且未达到最大重试次数,继续重试(指数退避)
|
||||
if (stillPending.length > 0 && retryCount < MAX_BALANCE_RETRIES) {
|
||||
const delay = 3000 * Math.pow(1.5, retryCount) // 3s, 4.5s, 6.75s
|
||||
const delay = Math.min(
|
||||
PENDING_BALANCE_RETRY_BASE_DELAY_MS * Math.pow(2, retryCount),
|
||||
PENDING_BALANCE_RETRY_MAX_DELAY_MS,
|
||||
)
|
||||
const timerId = setTimeout(() => {
|
||||
pendingTimers.delete(timerId)
|
||||
// 检查版本号,确保没有新的加载请求
|
||||
|
||||
Reference in New Issue
Block a user