refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate

- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦
- 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块
- 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支
- 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合
- 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor
- 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
fawney19
2026-04-07 02:50:19 +08:00
parent 763ff03a7b
commit 5d96d6673b
732 changed files with 28589 additions and 20662 deletions
@@ -1,20 +1,29 @@
use std::collections::{BTreeMap, BTreeSet};
use aether_data::repository::candidates::StoredRequestCandidate;
use aether_data::repository::provider_catalog::StoredProviderCatalogKey;
use aether_data_contracts::repository::provider_catalog::StoredProviderCatalogKey;
use aether_scheduler_core::{
build_provider_concurrent_limit_map, candidate_is_selectable_with_runtime_state,
collect_selectable_candidates_from_keys,
reorder_candidates_by_scheduler_health as reorder_candidates_by_scheduler_health_in_core,
SchedulerAffinityTarget,
};
use crate::data::auth::GatewayAuthApiKeySnapshot;
use crate::data::candidate_selection::{
read_minimal_candidate_selection, MinimalCandidateSelectionRowSource,
};
use crate::scheduler::affinity::SCHEDULER_AFFINITY_TTL;
use crate::GatewayError;
use super::{GatewayMinimalCandidateSelectionCandidate, SchedulerRuntimeState};
use super::affinity::{
build_scheduler_affinity_cache_key, candidate_key, remember_scheduler_affinity,
};
use super::runtime::{
auth_snapshot_concurrency_limit_reached, is_candidate_selectable,
read_candidate_runtime_selection_snapshot,
};
use super::{SchedulerMinimalCandidateSelectionCandidate, SchedulerRuntimeState};
pub(super) fn reorder_candidates_by_scheduler_health(
candidates: &mut [GatewayMinimalCandidateSelectionCandidate],
candidates: &mut [SchedulerMinimalCandidateSelectionCandidate],
provider_key_rpm_states: &BTreeMap<String, StoredProviderCatalogKey>,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
) {
@@ -28,73 +37,89 @@ pub(super) fn reorder_candidates_by_scheduler_health(
);
}
pub(super) use aether_scheduler_core::should_skip_provider_quota;
pub(super) async fn is_candidate_selectable(
candidate: &GatewayMinimalCandidateSelectionCandidate,
recent_candidates: &[StoredRequestCandidate],
provider_concurrent_limits: &BTreeMap<String, usize>,
provider_key_rpm_states: &BTreeMap<String, StoredProviderCatalogKey>,
#[cfg_attr(not(test), allow(dead_code))]
pub(super) async fn select_minimal_candidate(
selection_row_source: &(impl MinimalCandidateSelectionRowSource + Sync),
runtime_state: &impl SchedulerRuntimeState,
api_format: &str,
global_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
now_unix_secs: u64,
cached_affinity_target: Option<&SchedulerAffinityTarget>,
state: &(impl SchedulerRuntimeState + ?Sized),
) -> Result<bool, GatewayError> {
let provider_quota_blocks_requests = state
.read_provider_quota_snapshot(&candidate.provider_id)
.await?
.as_ref()
.is_some_and(|quota| should_skip_provider_quota(quota, now_unix_secs));
let rpm_reset_at = state.provider_key_rpm_reset_at(candidate.key_id.as_str(), now_unix_secs);
Ok(candidate_is_selectable_with_runtime_state(
candidate,
recent_candidates,
provider_concurrent_limits,
provider_key_rpm_states,
) -> Result<Option<SchedulerMinimalCandidateSelectionCandidate>, GatewayError> {
let affinity_cache_key =
build_scheduler_affinity_cache_key(auth_snapshot, api_format, global_model_name);
let selected = collect_selectable_candidates(
selection_row_source,
runtime_state,
api_format,
global_model_name,
require_streaming,
auth_snapshot,
now_unix_secs,
cached_affinity_target,
provider_quota_blocks_requests,
rpm_reset_at,
)
.await?
.into_iter()
.next();
if let Some(candidate) = selected.as_ref() {
remember_scheduler_affinity(affinity_cache_key.as_deref(), runtime_state, candidate);
}
Ok(selected)
}
pub(super) async fn collect_selectable_candidates(
selection_row_source: &(impl MinimalCandidateSelectionRowSource + Sync),
runtime_state: &impl SchedulerRuntimeState,
api_format: &str,
global_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
now_unix_secs: u64,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, GatewayError> {
let mut candidates = read_minimal_candidate_selection(
selection_row_source,
api_format,
global_model_name,
require_streaming,
auth_snapshot,
)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))?;
let runtime_snapshot =
read_candidate_runtime_selection_snapshot(runtime_state, &candidates, now_unix_secs)
.await?;
reorder_candidates_by_scheduler_health(
&mut candidates,
&runtime_snapshot.provider_key_rpm_states,
auth_snapshot,
);
let affinity_cache_key =
build_scheduler_affinity_cache_key(auth_snapshot, api_format, global_model_name);
let cached_affinity_target = affinity_cache_key.as_deref().and_then(|cache_key| {
runtime_state.read_cached_scheduler_affinity_target(cache_key, SCHEDULER_AFFINITY_TTL)
});
if auth_snapshot_concurrency_limit_reached(auth_snapshot, &runtime_snapshot, now_unix_secs) {
return Ok(Vec::new());
}
let mut selected_keys = BTreeSet::new();
for candidate in &candidates {
if !is_candidate_selectable(
candidate,
&runtime_snapshot,
now_unix_secs,
cached_affinity_target.as_ref(),
) {
continue;
}
selected_keys.insert(candidate_key(candidate));
}
Ok(collect_selectable_candidates_from_keys(
candidates,
&selected_keys,
cached_affinity_target.as_ref(),
))
}
pub(super) async fn read_provider_concurrent_limits(
state: &(impl SchedulerRuntimeState + ?Sized),
candidates: &[GatewayMinimalCandidateSelectionCandidate],
) -> Result<BTreeMap<String, usize>, GatewayError> {
let provider_ids = candidates
.iter()
.map(|candidate| candidate.provider_id.clone())
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>();
if provider_ids.is_empty() {
return Ok(BTreeMap::new());
}
let providers = state
.read_provider_catalog_providers_by_ids(&provider_ids)
.await?;
Ok(build_provider_concurrent_limit_map(providers))
}
pub(super) async fn read_provider_key_rpm_states(
state: &(impl SchedulerRuntimeState + ?Sized),
candidates: &[GatewayMinimalCandidateSelectionCandidate],
) -> Result<BTreeMap<String, StoredProviderCatalogKey>, GatewayError> {
let key_ids = candidates
.iter()
.map(|candidate| candidate.key_id.clone())
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>();
if key_ids.is_empty() {
return Ok(BTreeMap::new());
}
let keys = state.read_provider_catalog_keys_by_ids(&key_ids).await?;
Ok(keys
.into_iter()
.map(|key| (key.id.clone(), key))
.collect::<BTreeMap<_, _>>())
}