feat: 全栈功能增强 - 扩展 provider/pool 管理、完善调度与数据层、重构前端 Pool 页面

后端:
- 扩展 pool_admin payloads 和 provider query models,增强 endpoint key 管理
- 完善 scheduler-core 候选排序与请求候选逻辑
- 增强 usage-runtime 写入、provider-transport 网络层与 OAuth 刷新
- 改进 AI pipeline 响应转换与流式处理
- 扩展 global_models/provider_catalog 数据层查询能力
- 增强 video-tasks-core 多 provider 支持
- 新增大量集成测试覆盖 pool/keys/provider_query/frontdoor

前端:
- 重构 PoolManagement 页面,拆分状态管理/对话框逻辑到独立模块
- 新增 poolAdvancedDialog/poolSchedulingDialog/poolManagementState/poolMobilePresentation 工具函数及测试
- 改进 Dialog 组件与 provider tabs 显示

部署:
- 更新 Rust CI workflow 和 Dockerfile 构建配置

Closes #275
Co-authored-by: AAEE86 <[email protected]>
This commit is contained in:
fawney19
2026-04-09 13:51:50 +08:00
parent 4fc95adfb9
commit b0b40c16ff
97 changed files with 5816 additions and 1881 deletions
@@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use std::sync::RwLock;
use async_trait::async_trait;
@@ -91,6 +92,35 @@ impl InMemoryGlobalModelReadRepository {
.expect("admin global model repository lock") = items.into_iter().collect();
self
}
fn admin_global_model_provider_counts(&self, global_model_id: &str) -> (u64, u64) {
let items = self
.admin_provider_model_items
.read()
.expect("admin provider model repository lock");
let provider_count = items
.iter()
.filter(|item| item.global_model_id == global_model_id)
.map(|item| item.provider_id.clone())
.collect::<BTreeSet<_>>()
.len() as u64;
let active_provider_count = items
.iter()
.filter(|item| item.global_model_id == global_model_id && item.is_active)
.map(|item| item.provider_id.clone())
.collect::<BTreeSet<_>>()
.len() as u64;
(provider_count, active_provider_count)
}
fn enrich_admin_global_model(&self, item: &StoredAdminGlobalModel) -> StoredAdminGlobalModel {
let mut enriched = item.clone();
let (provider_count, active_provider_count) =
self.admin_global_model_provider_counts(&item.id);
enriched.provider_count = provider_count;
enriched.active_provider_count = active_provider_count;
enriched
}
}
#[async_trait]
@@ -260,6 +290,7 @@ impl GlobalModelReadRepository for InMemoryGlobalModelReadRepository {
.into_iter()
.skip(query.offset)
.take(query.limit)
.map(|item| self.enrich_admin_global_model(&item))
.collect();
Ok(StoredAdminGlobalModelPage { items, total })
}
@@ -354,7 +385,7 @@ impl GlobalModelReadRepository for InMemoryGlobalModelReadRepository {
Ok(items
.iter()
.find(|item| item.id == global_model_id)
.cloned())
.map(|item| self.enrich_admin_global_model(item)))
}
async fn get_admin_global_model_by_name(
@@ -365,7 +396,10 @@ impl GlobalModelReadRepository for InMemoryGlobalModelReadRepository {
.admin_global_model_items
.read()
.expect("admin global model repository lock");
Ok(items.iter().find(|item| item.name == model_name).cloned())
Ok(items
.iter()
.find(|item| item.name == model_name)
.map(|item| self.enrich_admin_global_model(item)))
}
async fn list_admin_provider_models_by_global_model_id(
@@ -537,35 +571,40 @@ impl GlobalModelWriteRepository for InMemoryGlobalModelReadRepository {
record.default_tiered_pricing.clone(),
record.supported_capabilities.clone(),
record.config.clone(),
0,
0,
0,
Some(1_711_000_000),
Some(1_711_000_000),
)?;
self.admin_global_model_items
.write()
.expect("admin global model repository lock")
.push(stored.clone());
Ok(Some(stored))
.push(stored);
self.get_admin_global_model_by_id(&record.id).await
}
async fn update_admin_global_model(
&self,
record: &UpdateAdminGlobalModelRecord,
) -> Result<Option<StoredAdminGlobalModel>, DataLayerError> {
let mut items = self
.admin_global_model_items
.write()
.expect("admin global model repository lock");
let Some(existing) = items.iter_mut().find(|item| item.id == record.id) else {
return Ok(None);
};
existing.display_name = record.display_name.clone();
existing.is_active = record.is_active;
existing.default_price_per_request = record.default_price_per_request;
existing.default_tiered_pricing = record.default_tiered_pricing.clone();
existing.supported_capabilities = record.supported_capabilities.clone();
existing.config = record.config.clone();
existing.updated_at_unix_secs = Some(1_711_000_100);
Ok(Some(existing.clone()))
{
let mut items = self
.admin_global_model_items
.write()
.expect("admin global model repository lock");
let Some(existing) = items.iter_mut().find(|item| item.id == record.id) else {
return Ok(None);
};
existing.display_name = record.display_name.clone();
existing.is_active = record.is_active;
existing.default_price_per_request = record.default_price_per_request;
existing.default_tiered_pricing = record.default_tiered_pricing.clone();
existing.supported_capabilities = record.supported_capabilities.clone();
existing.config = record.config.clone();
existing.updated_at_unix_secs = Some(1_711_000_100);
}
self.get_admin_global_model_by_id(&record.id).await
}
async fn delete_admin_global_model(
@@ -104,22 +104,39 @@ LEFT JOIN global_models gm ON gm.id = m.global_model_id
const LIST_ADMIN_GLOBAL_MODELS_PREFIX: &str = r#"
SELECT
id,
name,
COALESCE(NULLIF(display_name, ''), name) AS display_name,
is_active,
CAST(default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
default_tiered_pricing,
supported_capabilities,
config,
EXTRACT(EPOCH FROM created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM updated_at)::bigint AS updated_at_unix_secs
FROM global_models
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
WHEN m.is_active = TRUE AND p.is_active = TRUE THEN m.provider_id
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
"#;
const COUNT_ADMIN_GLOBAL_MODELS_PREFIX: &str = r#"
SELECT COUNT(id) AS total
FROM global_models
FROM global_models gm
"#;
const LIST_ACTIVE_GLOBAL_MODEL_IDS_BY_PROVIDER_IDS_PREFIX: &str = r#"
@@ -374,19 +391,35 @@ ORDER BY gm.name ASC, m.created_at DESC, m.id ASC
let row = sqlx::query(
r#"
SELECT
id,
name,
COALESCE(NULLIF(display_name, ''), name) AS display_name,
is_active,
CAST(default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
default_tiered_pricing,
supported_capabilities,
config
,
EXTRACT(EPOCH FROM created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM updated_at)::bigint AS updated_at_unix_secs
FROM global_models
WHERE id = $1
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
WHEN m.is_active = TRUE AND p.is_active = TRUE THEN m.provider_id
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
WHERE gm.id = $1
LIMIT 1
"#,
)
@@ -405,19 +438,35 @@ LIMIT 1
let row = sqlx::query(
r#"
SELECT
id,
name,
COALESCE(NULLIF(display_name, ''), name) AS display_name,
is_active,
CAST(default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
default_tiered_pricing,
supported_capabilities,
config
,
EXTRACT(EPOCH FROM created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM updated_at)::bigint AS updated_at_unix_secs
FROM global_models
WHERE name = $1
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_secs,
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
WHEN m.is_active = TRUE AND p.is_active = TRUE THEN m.provider_id
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
WHERE gm.name = $1
LIMIT 1
"#,
)
@@ -928,7 +977,7 @@ fn apply_admin_global_model_filters(
) {
builder.push(" WHERE 1=1");
if let Some(is_active) = query.is_active {
builder.push(" AND is_active = ").push_bind(is_active);
builder.push(" AND gm.is_active = ").push_bind(is_active);
}
if let Some(search) = query
.search
@@ -938,9 +987,9 @@ fn apply_admin_global_model_filters(
{
let pattern = format!("%{search}%");
builder
.push(" AND (name ILIKE ")
.push(" AND (gm.name ILIKE ")
.push_bind(pattern.clone())
.push(" OR display_name ILIKE ")
.push(" OR gm.display_name ILIKE ")
.push_bind(pattern)
.push(")");
}
@@ -1062,6 +1111,18 @@ fn map_admin_global_model_row(row: &PgRow) -> Result<StoredAdminGlobalModel, Dat
.try_get::<Option<i64>, _>("updated_at_unix_secs")
.map_postgres_err()?
.map(|value| value.max(0) as u64);
let provider_count = row
.try_get::<i64, _>("provider_count")
.map_postgres_err()?
.max(0) as u64;
let active_provider_count = row
.try_get::<i64, _>("active_provider_count")
.map_postgres_err()?
.max(0) as u64;
let usage_count = row
.try_get::<i64, _>("usage_count")
.map_postgres_err()?
.max(0) as u64;
StoredAdminGlobalModel::new(
row.try_get("id").map_postgres_err()?,
row.try_get("name").map_postgres_err()?,
@@ -1072,6 +1133,9 @@ fn map_admin_global_model_row(row: &PgRow) -> Result<StoredAdminGlobalModel, Dat
row.try_get("default_tiered_pricing").map_postgres_err()?,
row.try_get("supported_capabilities").map_postgres_err()?,
row.try_get("config").map_postgres_err()?,
provider_count,
active_provider_count,
usage_count,
created_at_unix_secs,
updated_at_unix_secs,
)