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 <53283266+Entropy-Xu@users.noreply.github.com>
This commit is contained in:
fawney19
2026-04-23 14:42:51 +08:00
parent f55f22d2e8
commit fa328e18a1
128 changed files with 5583 additions and 690 deletions

View File

@@ -1,5 +1,8 @@
use crate::handlers::shared::{json_string_list, unix_secs_to_rfc3339};
use crate::provider_key_auth::provider_key_auth_semantics;
use crate::provider_key_auth::{
provider_key_auth_semantics, provider_key_configured_api_formats,
provider_key_inherits_provider_api_formats,
};
use crate::AppState;
use aether_admin::provider::quota as admin_provider_quota_pure;
use aether_admin::provider::status as admin_provider_status_pure;
@@ -18,17 +21,18 @@ const OAUTH_REQUEST_FAILED_PREFIX: &str = "[REQUEST_FAILED] ";
pub(crate) fn provider_catalog_key_supports_format(
key: &StoredProviderCatalogKey,
provider_type: &str,
api_format: &str,
) -> bool {
let Some(value) = key.api_formats.as_ref() else {
if provider_key_inherits_provider_api_formats(key, provider_type) {
return true;
};
let Some(values) = value.as_array() else {
}
let formats = provider_key_configured_api_formats(key);
if formats.is_empty() {
return true;
};
values
}
formats
.iter()
.filter_map(serde_json::Value::as_str)
.any(|candidate| candidate.trim().eq_ignore_ascii_case(api_format))
}
@@ -1188,6 +1192,7 @@ pub(crate) fn build_admin_provider_key_response(
state: &AppState,
key: &StoredProviderCatalogKey,
provider_type: &str,
api_formats: &[String],
now_unix_secs: u64,
) -> serde_json::Value {
let request_count = u64::from(key.request_count.unwrap_or(0));
@@ -1245,8 +1250,9 @@ pub(crate) fn build_admin_provider_key_response(
payload.insert(
"api_formats".to_string(),
serde_json::Value::Array(
json_string_list(key.api_formats.as_ref())
.into_iter()
api_formats
.iter()
.cloned()
.map(serde_json::Value::String)
.collect(),
),