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:
fawney19
2026-04-23 14:42:51 +08:00
co-authored by Entropy.Xu
parent f55f22d2e8
commit fa328e18a1
128 changed files with 5583 additions and 690 deletions
@@ -14,6 +14,9 @@ use crate::ai_pipeline::{maybe_build_sync_finalize_outcome, GatewayControlDecisi
use crate::execution_runtime;
use crate::handlers::admin::request::{AdminAppState, AdminGatewayProviderTransportSnapshot};
use crate::model_fetch::ModelFetchRuntimeState;
use crate::provider_key_auth::{
provider_key_configured_api_formats, provider_key_inherits_provider_api_formats,
};
use crate::provider_transport::kiro::{
build_kiro_generate_assistant_response_url, build_kiro_provider_headers,
build_kiro_provider_request_body, supports_local_kiro_request_transport_with_network,
@@ -272,9 +275,13 @@ fn provider_query_select_kiro_endpoint<'a>(
fn provider_query_key_supports_endpoint(
key: &StoredProviderCatalogKey,
provider_type: &str,
endpoint_api_format: &str,
) -> bool {
let formats = json_string_list(key.api_formats.as_ref());
if provider_key_inherits_provider_api_formats(key, provider_type) {
return true;
}
let formats = provider_key_configured_api_formats(key);
formats.is_empty()
|| formats
.iter()
@@ -321,7 +328,11 @@ async fn provider_query_select_preferred_non_kiro_endpoint(
for key in keys {
if !key.is_active
|| selected_key_id.is_some_and(|value| value != key.id.as_str())
|| !provider_query_key_supports_endpoint(key, &endpoint.api_format)
|| !provider_query_key_supports_endpoint(
key,
&provider.provider_type,
&endpoint.api_format,
)
{
continue;
}
@@ -348,7 +359,11 @@ async fn provider_query_select_preferred_non_kiro_endpoint(
for key in keys {
if !key.is_active
|| selected_key_id.is_some_and(|value| value != key.id.as_str())
|| !provider_query_key_supports_endpoint(key, &endpoint.api_format)
|| !provider_query_key_supports_endpoint(
key,
&provider.provider_type,
&endpoint.api_format,
)
{
continue;
}
@@ -375,7 +390,11 @@ async fn provider_query_select_preferred_non_kiro_endpoint(
&& keys.iter().any(|key| {
key.is_active
&& selected_key_id.is_none_or(|value| value == key.id.as_str())
&& provider_query_key_supports_endpoint(key, &endpoint.api_format)
&& provider_query_key_supports_endpoint(
key,
&provider.provider_type,
&endpoint.api_format,
)
})
})
.or_else(|| endpoints.iter().find(|endpoint| endpoint.is_active))
@@ -531,7 +550,13 @@ async fn provider_query_build_kiro_test_candidates(
ADMIN_PROVIDER_QUERY_API_KEY_NOT_FOUND_DETAIL,
));
};
if !key.is_active || !provider_query_key_supports_endpoint(key, &endpoint.api_format) {
if !key.is_active
|| !provider_query_key_supports_endpoint(
key,
&provider.provider_type,
&endpoint.api_format,
)
{
return Err(build_admin_provider_query_not_found_response(
ADMIN_PROVIDER_QUERY_NO_ACTIVE_TEST_CANDIDATE_DETAIL,
));
@@ -567,7 +592,9 @@ async fn provider_query_build_kiro_test_candidates(
.as_deref()
.is_none_or(|value| value == key.id.as_str())
})
.filter(|key| provider_query_key_supports_endpoint(key, &endpoint.api_format))
.filter(|key| {
provider_query_key_supports_endpoint(key, &provider.provider_type, &endpoint.api_format)
})
.collect::<Vec<_>>();
keys.sort_by_key(|key| {
provider_query_test_key_sort_key(provider.provider_type.as_str(), key, &endpoint.api_format)