mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
245 lines
8.1 KiB
Rust
245 lines
8.1 KiB
Rust
|
|
use std::collections::BTreeSet;
|
|||
|
|
|
|||
|
|
use aether_pool_core::PoolSchedulingPreset;
|
|||
|
|
use serde_json::{json, Value};
|
|||
|
|
|
|||
|
|
use crate::capability::ProviderPoolCapability;
|
|||
|
|
use crate::provider::ProviderPoolAdapter;
|
|||
|
|
use crate::service::ProviderPoolService;
|
|||
|
|
|
|||
|
|
pub fn normalize_provider_scheduling_presets(
|
|||
|
|
adapter: &dyn ProviderPoolAdapter,
|
|||
|
|
scheduling_presets: &[PoolSchedulingPreset],
|
|||
|
|
) -> Vec<PoolSchedulingPreset> {
|
|||
|
|
let mut entries = Vec::<(usize, PoolSchedulingPreset)>::new();
|
|||
|
|
let mut seen = BTreeSet::new();
|
|||
|
|
|
|||
|
|
for (index, item) in scheduling_presets.iter().enumerate() {
|
|||
|
|
let preset = item.preset.trim().to_ascii_lowercase();
|
|||
|
|
if preset.is_empty() || !seen.insert(preset.clone()) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if !provider_pool_supports_preset(adapter, &preset) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
entries.push((
|
|||
|
|
index,
|
|||
|
|
PoolSchedulingPreset {
|
|||
|
|
preset,
|
|||
|
|
enabled: item.enabled,
|
|||
|
|
mode: item.mode.clone(),
|
|||
|
|
},
|
|||
|
|
));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if !entries.is_empty() {
|
|||
|
|
for preset in adapter.default_scheduling_presets() {
|
|||
|
|
let preset_name = preset.preset.trim().to_ascii_lowercase();
|
|||
|
|
if preset_name.is_empty() || seen.contains(&preset_name) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if !provider_pool_supports_preset(adapter, &preset_name) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
seen.insert(preset_name.clone());
|
|||
|
|
entries.push((
|
|||
|
|
entries.len(),
|
|||
|
|
PoolSchedulingPreset {
|
|||
|
|
preset: preset_name,
|
|||
|
|
enabled: preset.enabled,
|
|||
|
|
mode: preset.mode,
|
|||
|
|
},
|
|||
|
|
));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let mut distribution_mode = None::<(usize, PoolSchedulingPreset)>;
|
|||
|
|
let mut strategy_presets = Vec::<(usize, PoolSchedulingPreset)>::new();
|
|||
|
|
|
|||
|
|
for (index, preset) in entries {
|
|||
|
|
if !preset.enabled {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if let Some(mutex_group) = provider_pool_preset_mutex_group(&preset.preset) {
|
|||
|
|
if mutex_group == "distribution_mode"
|
|||
|
|
&& distribution_mode
|
|||
|
|
.as_ref()
|
|||
|
|
.is_none_or(|current| index < current.0)
|
|||
|
|
{
|
|||
|
|
distribution_mode = Some((index, preset));
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
strategy_presets.push((index, preset));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let mut normalized = Vec::new();
|
|||
|
|
if let Some((_, preset)) = distribution_mode.filter(|(_, preset)| preset.preset != "lru") {
|
|||
|
|
normalized.push(preset);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
strategy_presets.sort_by_key(|left| left.0);
|
|||
|
|
normalized.extend(strategy_presets.into_iter().map(|(_, preset)| preset));
|
|||
|
|
normalized
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub fn build_admin_pool_scheduling_presets_payload() -> Value {
|
|||
|
|
let service = ProviderPoolService::with_builtin_adapters();
|
|||
|
|
json!([
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"lru",
|
|||
|
|
"LRU 轮转",
|
|||
|
|
"最久未使用的 Key 优先",
|
|||
|
|
None,
|
|||
|
|
"依据 LRU 时间戳(最近未使用优先)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"cache_affinity",
|
|||
|
|
"缓存亲和",
|
|||
|
|
"优先复用最近使用过的 Key,利用 Prompt Caching",
|
|||
|
|
None,
|
|||
|
|
"依据 LRU 时间戳(最近使用优先,与 LRU 轮转相反)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"cost_first",
|
|||
|
|
"成本优先",
|
|||
|
|
"优先选择窗口消耗更低的账号",
|
|||
|
|
None,
|
|||
|
|
"依据窗口成本/Token 用量,缺失时回退配额使用率",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"free_first",
|
|||
|
|
"Free 优先",
|
|||
|
|
"优先消耗 Free 账号(依赖 plan_type)",
|
|||
|
|
Some(ProviderPoolCapability::PlanTier),
|
|||
|
|
"依据 plan_type(Free 账号优先调度)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"health_first",
|
|||
|
|
"健康优先",
|
|||
|
|
"优先选择健康分更高、失败更少的账号",
|
|||
|
|
None,
|
|||
|
|
"依据 health_by_format 聚合分(含熔断/失败衰减)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"latency_first",
|
|||
|
|
"延迟优先",
|
|||
|
|
"优先选择最近延迟更低的账号",
|
|||
|
|
None,
|
|||
|
|
"依据号池延迟窗口均值(latency_window_seconds)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"load_balance",
|
|||
|
|
"负载均衡",
|
|||
|
|
"随机分散 Key 使用,均匀分摊负载",
|
|||
|
|
None,
|
|||
|
|
"每次随机分值,实现完全均匀分散",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"plus_first",
|
|||
|
|
"Plus 优先",
|
|||
|
|
"优先消耗 Plus 账号(依赖 plan_type)",
|
|||
|
|
Some(ProviderPoolCapability::PlanTier),
|
|||
|
|
"依据 plan_type(Plus 账号优先调度)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"pro_first",
|
|||
|
|
"Pro 优先",
|
|||
|
|
"优先消耗 Pro 账号(依赖 plan_type)",
|
|||
|
|
Some(ProviderPoolCapability::PlanTier),
|
|||
|
|
"依据 plan_type(Pro 账号优先调度)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"priority_first",
|
|||
|
|
"优先级优先",
|
|||
|
|
"按账号优先级顺序调度(数字越小越优先)",
|
|||
|
|
None,
|
|||
|
|
"依据 internal_priority(支持拖拽/手工编辑)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"quota_balanced",
|
|||
|
|
"额度平均",
|
|||
|
|
"优先选额度消耗最少的账号",
|
|||
|
|
None,
|
|||
|
|
"依据账号配额使用率;无配额时回退到窗口成本使用",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"recent_refresh",
|
|||
|
|
"额度刷新优先",
|
|||
|
|
"优先选即将刷新额度的账号",
|
|||
|
|
Some(ProviderPoolCapability::QuotaReset),
|
|||
|
|
"依据账号额度重置倒计时(next_reset / reset_seconds)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"single_account",
|
|||
|
|
"单号优先",
|
|||
|
|
"集中使用同一账号(反向 LRU)",
|
|||
|
|
None,
|
|||
|
|
"先按账号优先级(internal_priority),同级再按反向 LRU 集中",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
provider_pool_preset_payload(
|
|||
|
|
"team_first",
|
|||
|
|
"Team 优先",
|
|||
|
|
"优先消耗 Team 账号(依赖 plan_type)",
|
|||
|
|
Some(ProviderPoolCapability::PlanTier),
|
|||
|
|
"依据 plan_type(Team 账号优先调度)",
|
|||
|
|
&service,
|
|||
|
|
),
|
|||
|
|
])
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn provider_pool_preset_payload(
|
|||
|
|
name: &'static str,
|
|||
|
|
label: &'static str,
|
|||
|
|
description: &'static str,
|
|||
|
|
capability: Option<ProviderPoolCapability>,
|
|||
|
|
evidence_hint: &'static str,
|
|||
|
|
service: &ProviderPoolService,
|
|||
|
|
) -> Value {
|
|||
|
|
let providers = capability
|
|||
|
|
.map(|capability| service.provider_types_for_capability(capability))
|
|||
|
|
.unwrap_or_default();
|
|||
|
|
json!({
|
|||
|
|
"name": name,
|
|||
|
|
"label": label,
|
|||
|
|
"description": description,
|
|||
|
|
"providers": providers,
|
|||
|
|
"modes": Value::Null,
|
|||
|
|
"default_mode": Value::Null,
|
|||
|
|
"mutex_group": provider_pool_preset_mutex_group(name),
|
|||
|
|
"evidence_hint": evidence_hint,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn provider_pool_supports_preset(adapter: &dyn ProviderPoolAdapter, preset: &str) -> bool {
|
|||
|
|
match preset {
|
|||
|
|
"free_first" | "plus_first" | "pro_first" | "team_first" => adapter
|
|||
|
|
.capabilities()
|
|||
|
|
.supports(ProviderPoolCapability::PlanTier),
|
|||
|
|
"recent_refresh" => adapter
|
|||
|
|
.capabilities()
|
|||
|
|
.supports(ProviderPoolCapability::QuotaReset),
|
|||
|
|
_ => true,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fn provider_pool_preset_mutex_group(preset: &str) -> Option<&'static str> {
|
|||
|
|
match preset {
|
|||
|
|
"lru" | "cache_affinity" | "load_balance" | "single_account" => Some("distribution_mode"),
|
|||
|
|
_ => None,
|
|||
|
|
}
|
|||
|
|
}
|