mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Fix/api key concurrency runtime miss (#309)
* test(cli): 覆盖 API key 并发等待与超时路径 * feat(scheduler): API key 并发饱和时等待可用槽位 * fix(proxy): 区分 API key 并发受限与真正的 runtime miss * fix(outcome): runtime miss 仅归因真实执行候选 * feat(api-keys): 统一 concurrent_limit 默认值与校验辅助 * feat(admin): 独立 Key 接口支持 concurrent_limit * feat(admin): 用户 API Key 路由支持 concurrent_limit * feat(public): 自助 API Key 路由支持 concurrent_limit * feat(import): 导入与存储层持久化 concurrent_limit * feat(frontend): 同步 API Key concurrent_limit 类型定义 * feat(frontend): 独立 Key 表单支持 concurrent_limit * feat(frontend): 管理员用户 API Key 表单支持 concurrent_limit * feat(frontend): 自助 API Key 页面支持 concurrent_limit * chore(fmt): 统一 runtime 归因相关 Rust 格式 * chore(fmt): 统一 admin API key 路由 Rust 格式 * chore(fmt): 统一 public 路由与相关测试 Rust 格式 * fix(test): 对齐 no-execution usage 归因断言 * test(middleware): 固定 access log tracing 用例线程模型 * fix(frontend): 提取用户 API Key payload 默认并发辅助 * fix(frontend): 保留用户 Key 的 concurrent_limit 默认值 * fix(api-keys): remove hardcoded concurrent limit default --------- Co-authored-by: fawney19 <elky0401@gmail.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import apiClient from './client'
|
||||
import { cachedRequest, buildCacheKey } from '@/utils/cache'
|
||||
import type { AxiosRequestConfig } from 'axios'
|
||||
import type { BillingSummary } from './auth'
|
||||
|
||||
// LDAP 配置导出结构
|
||||
@@ -353,6 +352,7 @@ export interface AdminApiKey {
|
||||
total_tokens?: number | null
|
||||
total_cost_usd?: number
|
||||
rate_limit?: number | null // null = 跟随系统默认,0 = 不限制
|
||||
concurrent_limit?: number | null // null = 跟随系统默认,0 = 不限制
|
||||
allowed_providers?: string[] | null // 允许的提供商列表
|
||||
allowed_api_formats?: string[] | null // 允许的 API 格式列表
|
||||
allowed_models?: string[] | null // 允许的模型列表
|
||||
@@ -370,6 +370,7 @@ export interface CreateStandaloneApiKeyRequest {
|
||||
allowed_api_formats?: string[] | null
|
||||
allowed_models?: string[] | null
|
||||
rate_limit?: number | null // null = 跟随系统默认,0 = 不限制
|
||||
concurrent_limit?: number | null // null = 跟随系统默认,0 = 不限制
|
||||
expires_at?: string | null // RFC3339 时间,null = 永不过期
|
||||
initial_balance_usd: number | null // 初始余额,null = 无限制
|
||||
unlimited_balance?: boolean | null // 编辑时仅切换额度模式,不调整余额数值
|
||||
@@ -594,7 +595,7 @@ export const adminApi = {
|
||||
key: string,
|
||||
value: unknown,
|
||||
description?: string,
|
||||
requestConfig?: AxiosRequestConfig,
|
||||
requestConfig?: Parameters<typeof apiClient.put>[2],
|
||||
): Promise<{ key: string; value: unknown; description?: string }> {
|
||||
const response = await apiClient.put<{ key: string; value: unknown; description?: string }>(
|
||||
`/api/admin/system/configs/${key}`,
|
||||
|
||||
@@ -159,6 +159,7 @@ export interface ApiKey {
|
||||
total_requests?: number
|
||||
total_cost_usd?: number
|
||||
rate_limit?: number | null
|
||||
concurrent_limit?: number | null
|
||||
allowed_providers?: ProviderConfig[]
|
||||
force_capabilities?: Record<string, boolean> | null // 强制能力配置
|
||||
}
|
||||
@@ -220,7 +221,7 @@ export const meApi = {
|
||||
return response.data
|
||||
},
|
||||
|
||||
async createApiKey(data: { name: string; rate_limit?: number }): Promise<ApiKey> {
|
||||
async createApiKey(data: { name: string; rate_limit?: number | null; concurrent_limit?: number | null }): Promise<ApiKey> {
|
||||
const response = await apiClient.post<ApiKey>('/api/users/me/api-keys', data)
|
||||
return response.data
|
||||
},
|
||||
@@ -253,7 +254,7 @@ export const meApi = {
|
||||
|
||||
async updateApiKey(
|
||||
keyId: string,
|
||||
data: { name?: string; rate_limit?: number | null }
|
||||
data: { name?: string; rate_limit?: number | null; concurrent_limit?: number | null }
|
||||
): Promise<ApiKey & { message: string }> {
|
||||
const response = await apiClient.put<ApiKey & { message: string }>(
|
||||
`/api/users/me/api-keys/${keyId}`,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import apiClient from './client'
|
||||
import type { UserSession as SessionRecord } from '@/types/session'
|
||||
|
||||
export interface User {
|
||||
id: string // UUID
|
||||
@@ -53,6 +54,7 @@ export interface ApiKey {
|
||||
is_locked: boolean // 管理员锁定标志
|
||||
is_standalone: boolean // 是否为独立余额Key
|
||||
rate_limit?: number | null // 普通Key: 0 = 不限制,历史 null 视为跟随系统默认
|
||||
concurrent_limit?: number | null // 普通Key: 0 = 不限制并发,历史 null 兼容
|
||||
total_requests?: number // 总请求数
|
||||
total_cost_usd?: number // 总费用
|
||||
}
|
||||
@@ -60,9 +62,10 @@ export interface ApiKey {
|
||||
export interface UpsertUserApiKeyRequest {
|
||||
name?: string
|
||||
rate_limit?: number | null
|
||||
concurrent_limit?: number | null
|
||||
}
|
||||
|
||||
export type { UserSession } from '@/types/session'
|
||||
export type UserSession = SessionRecord
|
||||
|
||||
export const usersApi = {
|
||||
async getAllUsers(): Promise<User[]> {
|
||||
@@ -94,8 +97,8 @@ export const usersApi = {
|
||||
return response.data.api_keys
|
||||
},
|
||||
|
||||
async getUserSessions(userId: string): Promise<UserSession[]> {
|
||||
const response = await apiClient.get<UserSession[]>(`/api/admin/users/${userId}/sessions`)
|
||||
async getUserSessions(userId: string): Promise<SessionRecord[]> {
|
||||
const response = await apiClient.get<SessionRecord[]>(`/api/admin/users/${userId}/sessions`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user