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 <[email protected]>
This commit is contained in:
RWDai
2026-04-17 14:21:43 +08:00
committed by GitHub
co-authored by fawney19
parent e5d3722adf
commit b8702ae124
39 changed files with 1739 additions and 128 deletions
@@ -13,6 +13,7 @@ use crate::handlers::admin::users::{
normalize_admin_optional_api_key_name, normalize_admin_user_api_formats,
normalize_admin_user_string_list,
};
use crate::handlers::shared::normalize_optional_api_key_concurrent_limit;
use crate::GatewayError;
use aether_admin::system::serialize_admin_system_users_export_wallet;
use axum::{
@@ -114,6 +115,11 @@ pub(super) async fn build_admin_create_api_key_response(
"rate_limit 必须大于等于 0",
));
}
let concurrent_limit =
match normalize_optional_api_key_concurrent_limit(payload.concurrent_limit) {
Ok(value) => value,
Err(detail) => return Ok(build_admin_api_keys_bad_request_response(detail)),
};
let (initial_balance_usd, unlimited_balance) = match normalize_standalone_initial_balance(
payload.initial_balance_usd,
payload.unlimited_balance,
@@ -154,7 +160,7 @@ pub(super) async fn build_admin_create_api_key_response(
allowed_api_formats,
allowed_models,
rate_limit: payload.rate_limit,
concurrent_limit: 5,
concurrent_limit,
force_capabilities: None,
is_active: true,
expires_at_unix_secs,
@@ -184,6 +190,7 @@ pub(super) async fn build_admin_create_api_key_response(
"is_standalone": true,
"is_active": created.is_active,
"rate_limit": created.rate_limit,
"concurrent_limit": created.concurrent_limit,
"allowed_providers": created.allowed_providers,
"allowed_api_formats": created.allowed_api_formats,
"allowed_models": created.allowed_models,
@@ -270,6 +277,11 @@ pub(super) async fn build_admin_update_api_key_response(
"rate_limit 必须大于等于 0",
));
}
let concurrent_limit =
match normalize_optional_api_key_concurrent_limit(payload.concurrent_limit) {
Ok(value) => value,
Err(detail) => return Ok(build_admin_api_keys_bad_request_response(detail)),
};
let allowed_providers = if field_presence.contains("allowed_providers") {
match normalize_admin_user_string_list(payload.allowed_providers, "allowed_providers") {
Ok(value) => Some(value),
@@ -356,6 +368,8 @@ pub(super) async fn build_admin_update_api_key_response(
name,
rate_limit_present: field_presence.contains("rate_limit"),
rate_limit: payload.rate_limit,
concurrent_limit_present: field_presence.contains("concurrent_limit"),
concurrent_limit,
allowed_providers,
allowed_api_formats,
allowed_models,
@@ -22,6 +22,7 @@ pub(super) struct AdminStandaloneApiKeyCreateRequest {
pub(super) allowed_api_formats: Option<Vec<String>>,
pub(super) allowed_models: Option<Vec<String>>,
pub(super) rate_limit: Option<i32>,
pub(super) concurrent_limit: Option<i32>,
pub(super) initial_balance_usd: Option<f64>,
pub(super) unlimited_balance: Option<bool>,
pub(super) expire_days: Option<i32>,
@@ -36,6 +37,7 @@ pub(super) struct AdminStandaloneApiKeyUpdateRequest {
pub(super) allowed_api_formats: Option<Vec<String>>,
pub(super) allowed_models: Option<Vec<String>>,
pub(super) rate_limit: Option<i32>,
pub(super) concurrent_limit: Option<i32>,
pub(super) initial_balance_usd: Option<f64>,
pub(super) unlimited_balance: Option<bool>,
pub(super) expire_days: Option<i32>,
@@ -144,6 +146,7 @@ pub(super) fn build_admin_api_key_list_item_payload(
"total_tokens": total_tokens,
"total_cost_usd": record.total_cost_usd,
"rate_limit": record.rate_limit,
"concurrent_limit": record.concurrent_limit,
"allowed_providers": record.allowed_providers,
"allowed_api_formats": record.allowed_api_formats,
"allowed_models": record.allowed_models,
@@ -173,6 +176,7 @@ pub(super) fn build_admin_api_key_detail_payload(
"total_tokens": total_tokens,
"total_cost_usd": record.total_cost_usd,
"rate_limit": record.rate_limit,
"concurrent_limit": record.concurrent_limit,
"allowed_providers": record.allowed_providers,
"allowed_api_formats": record.allowed_api_formats,
"allowed_models": record.allowed_models,